diff options
Diffstat (limited to 'src/test')
67 files changed, 9209 insertions, 1153 deletions
diff --git a/src/test/bench.c b/src/test/bench.c index 30984fda70..99bc686f30 100644 --- a/src/test/bench.c +++ b/src/test/bench.c @@ -120,7 +120,7 @@ bench_onion_TAP(void) uint64_t start, end; char os[TAP_ONIONSKIN_CHALLENGE_LEN]; char or[TAP_ONIONSKIN_REPLY_LEN]; - crypto_dh_t *dh_out; + crypto_dh_t *dh_out = NULL; key = crypto_pk_new(); key2 = crypto_pk_new(); @@ -175,6 +175,7 @@ bench_onion_TAP(void) NANOCOUNT(start, end, iters)/1e3); done: + crypto_dh_free(dh_out); crypto_pk_free(key); crypto_pk_free(key2); } diff --git a/src/test/fuzz/dict/consensus b/src/test/fuzz/dict/consensus new file mode 100644 index 0000000000..3fcd9ee7ff --- /dev/null +++ b/src/test/fuzz/dict/consensus @@ -0,0 +1,52 @@ +"a" +"additional-digest" +"additional-signature" +"bandwidth-weights" +"client-versions" +"consensus-digest" +"consensus-method" +"consensus-methods" +"contact" +"dir-address" +"directory-footer" +"directory-signature" +"dir-identity-key" +"dir-key-certificate-version" +"dir-key-certification" +"dir-key-crosscert" +"dir-key-expires" +"dir-key-published" +"dir-signing-key" +"dir-source" +"fingerprint" +"fresh-until" +"id" +"known-flags" +"legacy-dir-key" +"m" +"network-status-version" +"opt" +"p" +"package" +"params" +"pr" +"published" +"r" +"recommended-client-protocols" +"recommended-relay-protocols" +"required-client-protocols" +"required-relay-protocols" +"s" +"server-versions" +"shared-rand-commit" +"shared-rand-current-value" +"shared-rand-participate" +"shared-rand-previous-value" +"signing-ed25519" +"v" +"valid-after" +"valid-until" +"vote-digest" +"vote-status" +"voting-delay" +"w" diff --git a/src/test/fuzz/dict/descriptor b/src/test/fuzz/dict/descriptor new file mode 100644 index 0000000000..110ee3e820 --- /dev/null +++ b/src/test/fuzz/dict/descriptor @@ -0,0 +1,41 @@ +"reject" +"accept" +"reject6" +"accept6" +"router" +"ipv6-policy" +"signing-key" +"onion-key" +"ntor-onion-key" +"router-signature" +"published" +"uptime" +"fingerprint" +"hibernating" +"platform" +"proto" +"contact" +"read-history" +"write-history" +"extra-info-digest" +"hidden-service-dir" +"identity-ed25519" +"master-key-ed25519" +"router-sig-ed25519" +"onion-key-crosscert" +"ntor-onion-key-crosscert" +"allow-single-hop-exits" +"family" +"caches-extra-info" +"or-address" +"opt" + "bandwidth" +"@purpose" +"tunnelled-dir-server" +"-----BEGIN" +"-----END" +"-----" +"ED25519 CERT" +"RSA PUBLIC KEY" +"CROSSCERT" +"SIGNATURE" diff --git a/src/test/fuzz/dict/extrainfo b/src/test/fuzz/dict/extrainfo new file mode 100644 index 0000000000..eba7a1e4ce --- /dev/null +++ b/src/test/fuzz/dict/extrainfo @@ -0,0 +1,32 @@ +"cell-circuits-per-decile" +"cell-processed-cells" +"cell-queued-cells" +"cell-stats-end" +"cell-time-in-queue" +"dirreq-stats-end" +"dirreq-v2-direct-dl" +"dirreq-v2-ips" +"dirreq-v2-reqs" +"dirreq-v2-resp" +"dirreq-v2-share" +"dirreq-v2-tunneled-dl" +"dirreq-v3-direct-dl" +"dirreq-v3-ips" +"dirreq-v3-reqs" +"dirreq-v3-resp" +"dirreq-v3-share" +"dirreq-v3-tunneled-dl" +"entry-ips" +"entry-stats-end" +"exit-kibibytes-read" +"exit-kibibytes-written" +"exit-stats-end" +"exit-streams-opened" +"extra-info" +"identity-ed25519" +"opt" +"published" +"read-history" +"router-sig-ed25519" +"router-signature" +"write-history" diff --git a/src/test/fuzz/dict/hsdescv2 b/src/test/fuzz/dict/hsdescv2 new file mode 100644 index 0000000000..48788301dc --- /dev/null +++ b/src/test/fuzz/dict/hsdescv2 @@ -0,0 +1,8 @@ +"introduction-points" +"permanent-key" +"protocol-versions" +"publication-time" +"rendezvous-service-descriptor" +"secret-id-part" +"signature" +"version" diff --git a/src/test/fuzz/dict/http b/src/test/fuzz/dict/http new file mode 100644 index 0000000000..1a7b61e8d4 --- /dev/null +++ b/src/test/fuzz/dict/http @@ -0,0 +1,24 @@ +# +# AFL dictionary for the Tor Directory protocol's HTTP headers +# ------------------------------------------------------------ +# +# Extracted from directory_handle_command() in the tor source code +# +# Copyright (c) 2016, The Tor Project, Inc. +# See LICENSE for licensing information +# +# Usage: +# Select the dictionaries relevant to the part of the directory protocol you +# are fuzzing, and feed them to your fuzzer (if it supports dictionaries). + +http_header_body_delimiter = "\x0d\x0a\x0d\x0a" +http_header_header_delimiter = "\x0d\x0a" +# multi-character tokens only +#http_header_value_delimiter = " " + +content_length_header = "Content-Length:" +forwarded_for_header = "Forwarded-For:" +x_forwarded_for_header = "X-Forwarded-For:" + +get_command = "GET" +post_command = "POST" diff --git a/src/test/fuzz/dict/iptsv2 b/src/test/fuzz/dict/iptsv2 new file mode 100644 index 0000000000..57791c5e3c --- /dev/null +++ b/src/test/fuzz/dict/iptsv2 @@ -0,0 +1,6 @@ +"introduction-point" +"ip-address" +"onion-port" +"onion-key" +"service-key" + diff --git a/src/test/fuzz/dict/microdesc b/src/test/fuzz/dict/microdesc new file mode 100644 index 0000000000..fdd0567b65 --- /dev/null +++ b/src/test/fuzz/dict/microdesc @@ -0,0 +1,7 @@ +"onion-key" +"ntor-onion-key" +"id" +"a" +"family" +"p" +"p6" diff --git a/src/test/fuzz/fixup_filenames.sh b/src/test/fuzz/fixup_filenames.sh new file mode 100755 index 0000000000..68efc1abc5 --- /dev/null +++ b/src/test/fuzz/fixup_filenames.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +set -e + +if [ ! -d "$1" ] ; then + echo "I need a directory" + exit 1 +fi + +for fn in "$1"/* ; do + prev=`basename "$fn"` + post=`sha256sum "$fn" | sed -e 's/ .*//;'` + if [ "$prev" == "$post" ] ; then + echo "OK $prev" + else + echo "mv $prev $post" + mv "$fn" "$1/$post" + fi +done diff --git a/src/test/fuzz/fuzz_consensus.c b/src/test/fuzz/fuzz_consensus.c new file mode 100644 index 0000000000..f5d22f69ae --- /dev/null +++ b/src/test/fuzz/fuzz_consensus.c @@ -0,0 +1,78 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "networkstatus.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +static int +mock_signed_digest_equals__yes(const uint8_t *d1, const uint8_t *d2, + size_t len) +{ + (void) tor_memeq(d1, d2, len); + return 1; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + MOCK(signed_digest_equals, mock_signed_digest_equals__yes); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + networkstatus_t *ns; + char *str = tor_memdup_nulterm(data, sz); + const char *eos = NULL; + networkstatus_type_t tp = NS_TYPE_CONSENSUS; + if (tor_memstr(data, MIN(sz, 1024), "tus vote")) + tp = NS_TYPE_VOTE; + const char *what = (tp == NS_TYPE_CONSENSUS) ? "consensus" : "vote"; + ns = networkstatus_parse_vote_from_string(str, + &eos, + tp); + if (ns) { + log_debug(LD_GENERAL, "Parsing as %s okay", what); + networkstatus_vote_free(ns); + } else { + log_debug(LD_GENERAL, "Parsing as %s failed", what); + } + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzz_descriptor.c b/src/test/fuzz/fuzz_descriptor.c new file mode 100644 index 0000000000..d19386d77f --- /dev/null +++ b/src/test/fuzz/fuzz_descriptor.c @@ -0,0 +1,79 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "routerlist.h" +#include "routerkeys.h" +#include "fuzzing.h" + +static int +mock_check_tap_onion_key_crosscert__nocheck(const uint8_t *crosscert, + int crosscert_len, + const crypto_pk_t *onion_pkey, + const ed25519_public_key_t *master_id_pkey, + const uint8_t *rsa_id_digest) +{ + tor_assert(crosscert && onion_pkey && master_id_pkey && rsa_id_digest); + /* we could look at crosscert[..] */ + (void) crosscert_len; + return 0; +} + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(check_tap_onion_key_crosscert, + mock_check_tap_onion_key_crosscert__nocheck); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + routerinfo_t *ri; + const char *str = (const char*) data; + ri = router_parse_entry_from_string((const char *)str, + str+sz, + 0, 0, 0, NULL); + if (ri) { + log_debug(LD_GENERAL, "Parsing okay"); + routerinfo_free(ri); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_extrainfo.c b/src/test/fuzz/fuzz_extrainfo.c new file mode 100644 index 0000000000..6251e606d0 --- /dev/null +++ b/src/test/fuzz/fuzz_extrainfo.c @@ -0,0 +1,65 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "routerlist.h" +#include "routerkeys.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static int +mock_router_produce_hash_final__nohash(char *digest, + const char *start, size_t len, + digest_algorithm_t alg) +{ + (void)start; + (void)len; + /* we could look at start[..] */ + if (alg == DIGEST_SHA1) + memset(digest, 0x01, 20); + else + memset(digest, 0x02, 32); + return 0; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + MOCK(router_compute_hash_final, mock_router_produce_hash_final__nohash); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + extrainfo_t *ei; + const char *str = (const char*) data; + int again = 0; + ei = extrainfo_parse_entry_from_string((const char *)str, + str+sz, + 0, NULL, &again); + if (ei) { + log_debug(LD_GENERAL, "Parsing okay"); + extrainfo_free(ei); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_hsdescv2.c b/src/test/fuzz/fuzz_hsdescv2.c new file mode 100644 index 0000000000..53b7cbe2f7 --- /dev/null +++ b/src/test/fuzz/fuzz_hsdescv2.c @@ -0,0 +1,52 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "rendcommon.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + rend_service_descriptor_t *desc = NULL; + char desc_id[64]; + char *ipts = NULL; + size_t ipts_size, esize; + const char *next; + char *str = tor_memdup_nulterm(data, sz); + (void) rend_parse_v2_service_descriptor(&desc, desc_id, &ipts, &ipts_size, + &esize, &next, str, 1); + if (desc) { + log_debug(LD_GENERAL, "Parsing okay"); + rend_service_descriptor_free(desc); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + tor_free(ipts); + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzz_http.c b/src/test/fuzz/fuzz_http.c new file mode 100644 index 0000000000..01c3815f18 --- /dev/null +++ b/src/test/fuzz/fuzz_http.c @@ -0,0 +1,133 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" + +#define BUFFERS_PRIVATE +#define DIRECTORY_PRIVATE + +#include "or.h" +#include "backtrace.h" +#include "buffers.h" +#include "config.h" +#include "connection.h" +#include "directory.h" +#include "torlog.h" + +#include "fuzzing.h" + +static void +mock_connection_write_to_buf_impl_(const char *string, size_t len, + connection_t *conn, int zlib) +{ + log_debug(LD_GENERAL, "%sResponse:\n%u\nConnection: %p\n%s\n", + zlib ? "Compressed " : "", (unsigned)len, conn, string); +} + +static int +mock_directory_handle_command_get(dir_connection_t *conn, + const char *headers, + const char *body, + size_t body_len) +{ + (void)conn; + + log_debug(LD_GENERAL, "Method:\nGET\n"); + + if (headers) { + log_debug(LD_GENERAL, "Header-Length:\n%u\n", (unsigned)strlen(headers)); + log_debug(LD_GENERAL, "Headers:\n%s\n", headers); + } + + log_debug(LD_GENERAL, "Body-Length:\n%u\n", (unsigned)body_len); + if (body) { + log_debug(LD_GENERAL, "Body:\n%s\n", body); + } + + /* Always tell the caller we succeeded */ + return 0; +} + +static int +mock_directory_handle_command_post(dir_connection_t *conn, + const char *headers, + const char *body, + size_t body_len) +{ + (void)conn; + + log_debug(LD_GENERAL, "Method:\nPOST\n"); + + if (headers) { + log_debug(LD_GENERAL, "Header-Length:\n%u\n", (unsigned)strlen(headers)); + log_debug(LD_GENERAL, "Headers:\n%s\n", headers); + } + + log_debug(LD_GENERAL, "Body-Length:\n%u\n", (unsigned)body_len); + if (body) { + log_debug(LD_GENERAL, "Body:\n%s\n", body); + } + + /* Always tell the caller we succeeded */ + return 0; +} + +int +fuzz_init(void) +{ + /* Set up fake response handler */ + MOCK(connection_write_to_buf_impl_, mock_connection_write_to_buf_impl_); + /* Set up the fake handler functions */ + MOCK(directory_handle_command_get, mock_directory_handle_command_get); + MOCK(directory_handle_command_post, mock_directory_handle_command_post); + + return 0; +} + +int +fuzz_cleanup(void) +{ + UNMOCK(connection_write_to_buf_impl_); + UNMOCK(directory_handle_command_get); + UNMOCK(directory_handle_command_post); + return 0; +} + +int +fuzz_main(const uint8_t *stdin_buf, size_t data_size) +{ + dir_connection_t dir_conn; + + /* Set up the fake connection */ + memset(&dir_conn, 0, sizeof(dir_connection_t)); + dir_conn.base_.type = CONN_TYPE_DIR; + /* Apparently tor sets this before directory_handle_command() is called. */ + dir_conn.base_.address = tor_strdup("replace-this-address.example.com"); + + dir_conn.base_.inbuf = buf_new_with_data((char*)stdin_buf, data_size); + if (!dir_conn.base_.inbuf) { + log_debug(LD_GENERAL, "Zero-Length-Input\n"); + goto done; + } + + /* Parse the headers */ + int rv = directory_handle_command(&dir_conn); + + /* TODO: check the output is correctly parsed based on the input */ + + /* Report the parsed origin address */ + if (dir_conn.base_.address) { + log_debug(LD_GENERAL, "Address:\n%s\n", dir_conn.base_.address); + } + + log_debug(LD_GENERAL, "Result:\n%d\n", rv); + + done: + /* Reset. */ + tor_free(dir_conn.base_.address); + buf_free(dir_conn.base_.inbuf); + dir_conn.base_.inbuf = NULL; + + return 0; +} + diff --git a/src/test/fuzz/fuzz_iptsv2.c b/src/test/fuzz/fuzz_iptsv2.c new file mode 100644 index 0000000000..341d4880bd --- /dev/null +++ b/src/test/fuzz/fuzz_iptsv2.c @@ -0,0 +1,46 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "rendcommon.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + rend_service_descriptor_t *desc = + tor_malloc_zero(sizeof(rend_service_descriptor_t)); + const char *str = (const char*) data; + int r = rend_parse_introduction_points(desc, str, sz); + if (r >= 0) { + log_debug(LD_GENERAL, "Parsing okay: %d", r); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + rend_service_descriptor_free(desc); + return 0; +} + diff --git a/src/test/fuzz/fuzz_microdesc.c b/src/test/fuzz/fuzz_microdesc.c new file mode 100644 index 0000000000..bb89546191 --- /dev/null +++ b/src/test/fuzz/fuzz_microdesc.c @@ -0,0 +1,47 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "microdesc.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + return 0; +} + +int +fuzz_cleanup(void) +{ + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + const char *str = (const char*) data; + smartlist_t *result = microdescs_parse_from_string((const char *)str, + str+sz, + 0, SAVED_NOWHERE, NULL); + if (result) { + log_debug(LD_GENERAL, "Parsing okay: %d", smartlist_len(result)); + SMARTLIST_FOREACH(result, microdesc_t *, md, microdesc_free(md)); + smartlist_free(result); + } else { + log_debug(LD_GENERAL, "Parsing failed"); + } + return 0; +} + diff --git a/src/test/fuzz/fuzz_multi.sh b/src/test/fuzz/fuzz_multi.sh new file mode 100755 index 0000000000..b4a17ed8cb --- /dev/null +++ b/src/test/fuzz/fuzz_multi.sh @@ -0,0 +1,34 @@ +MEMLIMIT_BYTES=21990500990976 + +N_CPUS=1 +if [ $# -ge 1 ]; then + N_CPUS="$1" + shift +fi + +FILTER=echo + +for i in `seq -w "$N_CPUS"`; do + if [ "$i" -eq 1 ]; then + if [ "$N_CPUS" -eq 1 ]; then + INSTANCE="" + NUMBER="" + else + INSTANCE="-M" + NUMBER="$i" + fi + else + INSTANCE="-S" + NUMBER="$i" + fi + # use whatever remains on the command-line to prefix the fuzzer command + # you have to copy and paste and run these commands yourself + "$FILTER" "$@" \ + ../afl/afl-fuzz \ + -i src/test/fuzz/fuzz_dir_testcase \ + -o src/test/fuzz/fuzz_dir_findings \ + -x src/test/fuzz/fuzz_dir_dictionary/fuzz_dir_http_header.dct \ + -m "$MEMLIMIT_BYTES" \ + "$INSTANCE" "$NUMBER" \ + -- src/test/fuzz_dir +done diff --git a/src/test/fuzz/fuzz_vrs.c b/src/test/fuzz/fuzz_vrs.c new file mode 100644 index 0000000000..9301a9bcc8 --- /dev/null +++ b/src/test/fuzz/fuzz_vrs.c @@ -0,0 +1,82 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define ROUTERPARSE_PRIVATE +#define NETWORKSTATUS_PRIVATE +#include "or.h" +#include "routerparse.h" +#include "memarea.h" +#include "microdesc.h" +#include "networkstatus.h" +#include "fuzzing.h" + +static void +mock_dump_desc__nodump(const char *desc, const char *type) +{ + (void)desc; + (void)type; +} + +static networkstatus_t *dummy_vote = NULL; +static memarea_t *area = NULL; + +int +fuzz_init(void) +{ + disable_signature_checking(); + MOCK(dump_desc, mock_dump_desc__nodump); + ed25519_init(); + area = memarea_new(); + dummy_vote = tor_malloc_zero(sizeof(*dummy_vote)); + dummy_vote->known_flags = smartlist_new(); + smartlist_split_string(dummy_vote->known_flags, + "Authority BadExit Exit Fast Guard HSDir " + "NoEdConsensus Running Stable V2Dir Valid", + " ", 0, 0); + return 0; +} + +int +fuzz_cleanup(void) +{ + SMARTLIST_FOREACH(dummy_vote->known_flags, char *, cp, tor_free(cp)); + smartlist_free(dummy_vote->known_flags); + tor_free(dummy_vote); + return 0; +} + +int +fuzz_main(const uint8_t *data, size_t sz) +{ + char *str = tor_memdup_nulterm(data, sz); + const char *s; + routerstatus_t *rs_ns = NULL, *rs_md = NULL, *rs_vote = NULL; + vote_routerstatus_t *vrs = tor_malloc_zero(sizeof(*vrs)); + smartlist_t *tokens = smartlist_new(); + + s = str; + rs_ns = routerstatus_parse_entry_from_string(area, &s, tokens, + NULL, NULL, 26, FLAV_NS); + tor_assert(smartlist_len(tokens) == 0); + + s = str; + rs_md = routerstatus_parse_entry_from_string(area, &s, tokens, + NULL, NULL, 26, FLAV_MICRODESC); + tor_assert(smartlist_len(tokens) == 0); + + s = str; + rs_vote = routerstatus_parse_entry_from_string(area, &s, tokens, + dummy_vote, vrs, 26, FLAV_NS); + tor_assert(smartlist_len(tokens) == 0); + + log_debug(LD_GENERAL, + "ns=%p, md=%p, vote=%p", rs_ns, rs_md, rs_vote); + + routerstatus_free(rs_md); + routerstatus_free(rs_ns); + vote_routerstatus_free(vrs); + memarea_clear(area); + smartlist_free(tokens); + tor_free(str); + return 0; +} + diff --git a/src/test/fuzz/fuzzing.h b/src/test/fuzz/fuzzing.h new file mode 100644 index 0000000000..4295743458 --- /dev/null +++ b/src/test/fuzz/fuzzing.h @@ -0,0 +1,13 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#ifndef FUZZING_H +#define FUZZING_H + +int fuzz_init(void); +int fuzz_cleanup(void); +int fuzz_main(const uint8_t *data, size_t sz); + +void disable_signature_checking(void); + +#endif /* FUZZING_H */ + diff --git a/src/test/fuzz/fuzzing_common.c b/src/test/fuzz/fuzzing_common.c new file mode 100644 index 0000000000..e4920d3ee7 --- /dev/null +++ b/src/test/fuzz/fuzzing_common.c @@ -0,0 +1,190 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ +#define CRYPTO_ED25519_PRIVATE +#include "orconfig.h" +#include "or.h" +#include "backtrace.h" +#include "config.h" +#include "fuzzing.h" +#include "crypto.h" +#include "crypto_ed25519.h" + +extern const char tor_git_revision[]; +const char tor_git_revision[] = ""; + +static or_options_t *mock_options = NULL; +static const or_options_t * +mock_get_options(void) +{ + return mock_options; +} + +static int +mock_crypto_pk_public_checksig__nocheck(const crypto_pk_t *env, char *to, + size_t tolen, + const char *from, size_t fromlen) +{ + tor_assert(env && to && from); + (void)fromlen; + /* We could look at from[0..fromlen-1] ... */ + tor_assert(tolen >= crypto_pk_keysize(env)); + memset(to, 0x01, 20); + return 20; +} + +static int +mock_crypto_pk_public_checksig_digest__nocheck(crypto_pk_t *env, + const char *data, + size_t datalen, + const char *sig, + size_t siglen) +{ + tor_assert(env && data && sig); + (void)datalen; + (void)siglen; + /* We could look at data[..] and sig[..] */ + return 0; +} + +static int +mock_ed25519_checksig__nocheck(const ed25519_signature_t *signature, + const uint8_t *msg, size_t len, + const ed25519_public_key_t *pubkey) +{ + tor_assert(signature && msg && pubkey); + /* We could look at msg[0..len-1] ... */ + (void)len; + return 0; +} + +static int +mock_ed25519_checksig_batch__nocheck(int *okay_out, + const ed25519_checkable_t *checkable, + int n_checkable) +{ + tor_assert(checkable); + int i; + for (i = 0; i < n_checkable; ++i) { + /* We could look at messages and signatures XXX */ + tor_assert(checkable[i].pubkey); + tor_assert(checkable[i].msg); + if (okay_out) + okay_out[i] = 1; + } + return 0; +} + +static int +mock_ed25519_impl_spot_check__nocheck(void) +{ + return 0; +} + +void +disable_signature_checking(void) +{ + MOCK(crypto_pk_public_checksig, + mock_crypto_pk_public_checksig__nocheck); + MOCK(crypto_pk_public_checksig_digest, + mock_crypto_pk_public_checksig_digest__nocheck); + MOCK(ed25519_checksig, mock_ed25519_checksig__nocheck); + MOCK(ed25519_checksig_batch, mock_ed25519_checksig_batch__nocheck); + MOCK(ed25519_impl_spot_check, mock_ed25519_impl_spot_check__nocheck); +} + +static void +global_init(void) +{ + tor_threads_init(); + { + struct sipkey sipkey = { 1337, 7331 }; + siphash_set_global_key(&sipkey); + } + + /* Initialise logging first */ + init_logging(1); + configure_backtrace_handler(get_version()); + + /* set up the options. */ + mock_options = tor_malloc(sizeof(or_options_t)); + MOCK(get_options, mock_get_options); + + /* Make BUG() and nonfatal asserts crash */ + tor_set_failed_assertion_callback(abort); +} + +#ifdef LLVM_FUZZ +int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size); +int +LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) +{ + static int initialized = 0; + if (!initialized) { + global_init(); + if (fuzz_init() < 0) + abort(); + initialized = 1; + } + + return fuzz_main(Data, Size); +} + +#else /* Not LLVM_FUZZ, so AFL. */ + +int +main(int argc, char **argv) +{ + size_t size; + + global_init(); + + /* Disable logging by default to speed up fuzzing. */ + int loglevel = LOG_ERR; + + for (int i = 1; i < argc; ++i) { + if (!strcmp(argv[i], "--warn")) { + loglevel = LOG_WARN; + } else if (!strcmp(argv[i], "--notice")) { + loglevel = LOG_NOTICE; + } else if (!strcmp(argv[i], "--info")) { + loglevel = LOG_INFO; + } else if (!strcmp(argv[i], "--debug")) { + loglevel = LOG_DEBUG; + } + } + + { + log_severity_list_t s; + memset(&s, 0, sizeof(s)); + set_log_severity_config(loglevel, LOG_ERR, &s); + /* ALWAYS log bug warnings. */ + s.masks[LOG_WARN-LOG_ERR] |= LD_BUG; + add_stream_log(&s, "", fileno(stdout)); + } + + if (fuzz_init() < 0) + abort(); + +#ifdef __AFL_HAVE_MANUAL_CONTROL + /* Tell AFL to pause and fork here - ignored if not using AFL */ + __AFL_INIT(); +#endif + +#define MAX_FUZZ_SIZE (128*1024) + char *input = read_file_to_str_until_eof(0, MAX_FUZZ_SIZE, &size); + tor_assert(input); + char *raw = tor_memdup(input, size); /* Because input is nul-terminated */ + tor_free(input); + fuzz_main((const uint8_t*)raw, size); + tor_free(raw); + + if (fuzz_cleanup() < 0) + abort(); + + tor_free(mock_options); + UNMOCK(get_options); + return 0; +} + +#endif + diff --git a/src/test/fuzz/include.am b/src/test/fuzz/include.am new file mode 100644 index 0000000000..806710879b --- /dev/null +++ b/src/test/fuzz/include.am @@ -0,0 +1,250 @@ + +FUZZING_CPPFLAGS = \ + $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) +FUZZING_CFLAGS = \ + $(AM_CFLAGS) $(TEST_CFLAGS) +FUZZING_LDFLAG = \ + @TOR_LDFLAGS_zlib@ @TOR_LDFLAGS_openssl@ @TOR_LDFLAGS_libevent@ +FUZZING_LIBS = \ + src/or/libtor-testing.a \ + src/common/libor-crypto-testing.a \ + $(LIBKECCAK_TINY) \ + $(LIBDONNA) \ + src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ + src/common/libor-event-testing.a \ + src/trunnel/libor-trunnel-testing.a \ + @TOR_ZLIB_LIBS@ @TOR_LIB_MATH@ \ + @TOR_LIBEVENT_LIBS@ \ + @TOR_OPENSSL_LIBS@ @TOR_LIB_WS32@ @TOR_LIB_GDI@ @CURVE25519_LIBS@ \ + @TOR_SYSTEMD_LIBS@ + +oss-fuzz-prereqs: \ + src/or/libtor-testing.a \ + src/common/libor-crypto-testing.a \ + $(LIBKECCAK_TINY) \ + $(LIBDONNA) \ + src/common/libor-testing.a \ + src/common/libor-ctime-testing.a \ + src/common/libor-event-testing.a \ + src/trunnel/libor-trunnel-testing.a + +noinst_HEADERS += \ + src/test/fuzz/fuzzing.h + +src_test_fuzz_fuzz_consensus_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_consensus.c +src_test_fuzz_fuzz_consensus_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_consensus_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_consensus_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_consensus_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_descriptor_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_descriptor.c +src_test_fuzz_fuzz_descriptor_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_descriptor_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_descriptor_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_descriptor_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_http_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_http.c +src_test_fuzz_fuzz_http_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_http_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_http_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_http_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_hsdescv2_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_hsdescv2.c +src_test_fuzz_fuzz_hsdescv2_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_hsdescv2_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_hsdescv2_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_hsdescv2_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_iptsv2_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_iptsv2.c +src_test_fuzz_fuzz_iptsv2_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_iptsv2_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_iptsv2_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_iptsv2_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_extrainfo_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_extrainfo.c +src_test_fuzz_fuzz_extrainfo_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_extrainfo_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_extrainfo_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_extrainfo_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_microdesc_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_microdesc.c +src_test_fuzz_fuzz_microdesc_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_microdesc_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_microdesc_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_microdesc_LDADD = $(FUZZING_LIBS) + +src_test_fuzz_fuzz_vrs_SOURCES = \ + src/test/fuzz/fuzzing_common.c \ + src/test/fuzz/fuzz_vrs.c +src_test_fuzz_fuzz_vrs_CPPFLAGS = $(FUZZING_CPPFLAGS) +src_test_fuzz_fuzz_vrs_CFLAGS = $(FUZZING_CFLAGS) +src_test_fuzz_fuzz_vrs_LDFLAGS = $(FUZZING_LDFLAG) +src_test_fuzz_fuzz_vrs_LDADD = $(FUZZING_LIBS) + +FUZZERS = \ + src/test/fuzz/fuzz-consensus \ + src/test/fuzz/fuzz-descriptor \ + src/test/fuzz/fuzz-extrainfo \ + src/test/fuzz/fuzz-http \ + src/test/fuzz/fuzz-hsdescv2 \ + src/test/fuzz/fuzz-iptsv2 \ + src/test/fuzz/fuzz-microdesc \ + src/test/fuzz/fuzz-vrs + + +LIBFUZZER = /home/nickm/build/libfuzz/libFuzzer.a +LIBFUZZER_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ +LIBFUZZER_CFLAGS = $(FUZZING_CFLAGS) +LIBFUZZER_LDFLAG = $(FUZZING_LDFLAG) +LIBFUZZER_LIBS = $(FUZZING_LIBS) $(LIBFUZZER) -lstdc++ + +if LIBFUZZER_ENABLED +src_test_fuzz_lf_fuzz_consensus_SOURCES = \ + $(src_test_fuzz_fuzz_consensus_SOURCES) +src_test_fuzz_lf_fuzz_consensus_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_consensus_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_consensus_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_consensus_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_descriptor_SOURCES = \ + $(src_test_fuzz_fuzz_descriptor_SOURCES) +src_test_fuzz_lf_fuzz_descriptor_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_descriptor_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_descriptor_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_descriptor_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_extrainfo_SOURCES = \ + $(src_test_fuzz_fuzz_extrainfo_SOURCES) +src_test_fuzz_lf_fuzz_extrainfo_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_extrainfo_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_extrainfo_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_extrainfo_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_http_SOURCES = \ + $(src_test_fuzz_fuzz_http_SOURCES) +src_test_fuzz_lf_fuzz_http_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_http_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_http_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_http_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_hsdescv2_SOURCES = \ + $(src_test_fuzz_fuzz_hsdescv2_SOURCES) +src_test_fuzz_lf_fuzz_hsdescv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_hsdescv2_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_hsdescv2_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_hsdescv2_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_iptsv2_SOURCES = \ + $(src_test_fuzz_fuzz_iptsv2_SOURCES) +src_test_fuzz_lf_fuzz_iptsv2_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_iptsv2_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_iptsv2_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_iptsv2_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_microdesc_SOURCES = \ + $(src_test_fuzz_fuzz_microdesc_SOURCES) +src_test_fuzz_lf_fuzz_microdesc_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_microdesc_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_microdesc_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_microdesc_LDADD = $(LIBFUZZER_LIBS) + +src_test_fuzz_lf_fuzz_vrs_SOURCES = \ + $(src_test_fuzz_fuzz_vrs_SOURCES) +src_test_fuzz_lf_fuzz_vrs_CPPFLAGS = $(LIBFUZZER_CPPFLAGS) +src_test_fuzz_lf_fuzz_vrs_CFLAGS = $(LIBFUZZER_CFLAGS) +src_test_fuzz_lf_fuzz_vrs_LDFLAGS = $(LIBFUZZER_LDFLAG) +src_test_fuzz_lf_fuzz_vrs_LDADD = $(LIBFUZZER_LIBS) + +LIBFUZZER_FUZZERS = \ + src/test/fuzz/lf-fuzz-consensus \ + src/test/fuzz/lf-fuzz-descriptor \ + src/test/fuzz/lf-fuzz-extrainfo \ + src/test/fuzz/lf-fuzz-http \ + src/test/fuzz/lf-fuzz-hsdescv2 \ + src/test/fuzz/lf-fuzz-iptsv2 \ + src/test/fuzz/lf-fuzz-microdesc \ + src/test/fuzz/lf-fuzz-vrs + +else +LIBFUZZER_FUZZERS = +endif + +if OSS_FUZZ_ENABLED +LIBOSS_FUZZ_CPPFLAGS = $(FUZZING_CPPFLAGS) -DLLVM_FUZZ +LIBOSS_FUZZ_CFLAGS = $(FUZZING_CFLAGS) + +src_test_fuzz_liboss_fuzz_consensus_a_SOURCES = \ + $(src_test_fuzz_fuzz_consensus_SOURCES) +src_test_fuzz_liboss_fuzz_consensus_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_consensus_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_descriptor_a_SOURCES = \ + $(src_test_fuzz_fuzz_descriptor_SOURCES) +src_test_fuzz_liboss_fuzz_descriptor_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_descriptor_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_extrainfo_a_SOURCES = \ + $(src_test_fuzz_fuzz_extrainfo_SOURCES) +src_test_fuzz_liboss_fuzz_extrainfo_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_extrainfo_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_http_a_SOURCES = \ + $(src_test_fuzz_fuzz_http_SOURCES) +src_test_fuzz_liboss_fuzz_http_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_http_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_hsdescv2_a_SOURCES = \ + $(src_test_fuzz_fuzz_hsdescv2_SOURCES) +src_test_fuzz_liboss_fuzz_hsdescv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_hsdescv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_iptsv2_a_SOURCES = \ + $(src_test_fuzz_fuzz_iptsv2_SOURCES) +src_test_fuzz_liboss_fuzz_iptsv2_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_iptsv2_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_microdesc_a_SOURCES = \ + $(src_test_fuzz_fuzz_microdesc_SOURCES) +src_test_fuzz_liboss_fuzz_microdesc_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_microdesc_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +src_test_fuzz_liboss_fuzz_vrs_a_SOURCES = \ + $(src_test_fuzz_fuzz_vrs_SOURCES) +src_test_fuzz_liboss_fuzz_vrs_a_CPPFLAGS = $(LIBOSS_FUZZ_CPPFLAGS) +src_test_fuzz_liboss_fuzz_vrs_a_CFLAGS = $(LIBOSS_FUZZ_CFLAGS) + +OSS_FUZZ_FUZZERS = \ + src/test/fuzz/liboss-fuzz-consensus.a \ + src/test/fuzz/liboss-fuzz-descriptor.a \ + src/test/fuzz/liboss-fuzz-extrainfo.a \ + src/test/fuzz/liboss-fuzz-http.a \ + src/test/fuzz/liboss-fuzz-hsdescv2.a \ + src/test/fuzz/liboss-fuzz-iptsv2.a \ + src/test/fuzz/liboss-fuzz-microdesc.a \ + src/test/fuzz/liboss-fuzz-vrs.a +else +OSS_FUZZ_FUZZERS = +endif + +noinst_PROGRAMS += $(FUZZERS) $(LIBFUZZER_FUZZERS) +noinst_LIBRARIES += $(OSS_FUZZ_FUZZERS) +oss-fuzz-fuzzers: oss-fuzz-prereqs $(OSS_FUZZ_FUZZERS) +fuzzers: $(FUZZERS) $(LIBFUZZER_FUZZERS) + +test-fuzz-corpora: $(FUZZERS) + $(top_srcdir)/src/test/fuzz_static_testcases.sh diff --git a/src/test/fuzz/minimize.sh b/src/test/fuzz/minimize.sh new file mode 100755 index 0000000000..87d3dda13c --- /dev/null +++ b/src/test/fuzz/minimize.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +set -e + +if [ ! -d "$1" ] ; then + echo "I need a directory" + exit 1 +fi + +which=`basename "$1"` + +mkdir "$1.out" +afl-cmin -i "$1" -o "$1.out" -m none "./src/test/fuzz/fuzz-${which}" + diff --git a/src/test/fuzz_static_testcases.sh b/src/test/fuzz_static_testcases.sh new file mode 100755 index 0000000000..bfe1677573 --- /dev/null +++ b/src/test/fuzz_static_testcases.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Copyright (c) 2016, The Tor Project, Inc. +# See LICENSE for licensing information + +set -e + +if [ -z "${TOR_FUZZ_CORPORA}" ] || [ ! -d "${TOR_FUZZ_CORPORA}" ] ; then + echo "You need to set TOR_FUZZ_CORPORA to point to a checkout of " + echo "the 'fuzzing-corpora' repository." + exit 77 +fi + + + +for fuzzer in "${builddir:-.}"/src/test/fuzz/fuzz-* ; do + f=`basename $fuzzer` + case="${f#fuzz-}" + if [ -d "${TOR_FUZZ_CORPORA}/${case}" ]; then + echo "Running tests for ${case}" + for entry in "${TOR_FUZZ_CORPORA}/${case}/"*; do + "${fuzzer}" "--err" < "$entry" + done + else + echo "No tests found for ${case}" + fi +done diff --git a/src/test/include.am b/src/test/include.am index 8ecfaf10c6..1c0726fd3a 100644 --- a/src/test/include.am +++ b/src/test/include.am @@ -8,7 +8,9 @@ TESTS_ENVIRONMENT = \ export builddir="$(builddir)"; \ export TESTING_TOR_BINARY="$(TESTING_TOR_BINARY)"; -TESTSCRIPTS = src/test/test_zero_length_keys.sh \ +TESTSCRIPTS = \ + src/test/fuzz_static_testcases.sh \ + src/test/test_zero_length_keys.sh \ src/test/test_workqueue_cancel.sh \ src/test/test_workqueue_efd.sh \ src/test/test_workqueue_efd2.sh \ @@ -80,6 +82,7 @@ src_test_test_SOURCES = \ src/test/test_checkdir.c \ src/test/test_circuitlist.c \ src/test/test_circuitmux.c \ + src/test/test_circuituse.c \ src/test/test_compat_libevent.c \ src/test/test_config.c \ src/test/test_connection.c \ @@ -96,7 +99,11 @@ src_test_test_SOURCES = \ src/test/test_guardfraction.c \ src/test/test_extorport.c \ src/test/test_hs.c \ + src/test/test_hs_service.c \ + src/test/test_hs_intropoint.c \ src/test/test_handles.c \ + src/test/test_hs_cache.c \ + src/test/test_hs_descriptor.c \ src/test/test_introduce.c \ src/test/test_keypin.c \ src/test/test_link_handshake.c \ @@ -130,6 +137,7 @@ src_test_test_SOURCES = \ src/test/test_helpers.c \ src/test/test_dns.c \ src/test/testing_common.c \ + src/test/testing_rsakeys.c \ src/ext/tinytest.c src_test_test_slow_SOURCES = \ @@ -137,6 +145,7 @@ src_test_test_slow_SOURCES = \ src/test/test_crypto_slow.c \ src/test/test_util_slow.c \ src/test/testing_common.c \ + src/test/testing_rsakeys.c \ src/ext/tinytest.c src_test_test_memwipe_SOURCES = \ @@ -262,6 +271,7 @@ src_test_test_bt_cl_CPPFLAGS= $(src_test_AM_CPPFLAGS) $(TEST_CPPFLAGS) EXTRA_DIST += \ src/test/bt_test.py \ src/test/ntor_ref.py \ + src/test/fuzz_static_testcases.sh \ src/test/slownacl_curve25519.py \ src/test/zero_length_keys.sh \ src/test/test_keygen.sh \ diff --git a/src/test/test-memwipe.c b/src/test/test-memwipe.c index 21882448c3..d3f2dc0644 100644 --- a/src/test/test-memwipe.c +++ b/src/test/test-memwipe.c @@ -36,7 +36,7 @@ const char *s = NULL; sum += (unsigned char)buf[i]; \ } -#ifdef __OpenBSD__ +#ifdef OpenBSD /* Disable some of OpenBSD's malloc protections for this test. This helps * us do bad things, such as access freed buffers, without crashing. */ const char *malloc_options="sufjj"; diff --git a/src/test/test-network.sh b/src/test/test-network.sh index 4d9776822b..10bd370ff3 100755 --- a/src/test/test-network.sh +++ b/src/test/test-network.sh @@ -1,8 +1,19 @@ -#! /bin/sh +#!/bin/sh + +# use bash if it is available, as this script doesn't work well in non-bash sh +# this will be fixed in #19699 +# there is no simple, portable way of checking the name of the shell, so we +# exec bash even when sh is bash +if [ -x /bin/bash -a "$USING_BASH" != true ]; then + # only do this once + export USING_BASH=true + exec /bin/bash "$0" "$@" +fi # Please do not modify this script, it has been moved to chutney/tools -ECHO_N="/bin/echo -n" +export ECHO="${ECHO:-echo}" +export ECHO_N="${ECHO_N:-/bin/echo -n}" # Output is prefixed with the name of the script myname=$(basename $0) @@ -19,23 +30,24 @@ if [ "$TEST_NETWORK_RECURSING" != true ]; then # When we switch to using test-network.sh in chutney/tools, --dry-run # can be removed, because this script will find chutney, then pass all # arguments to chutney's test-network.sh - echo "$myname: Parsing command-line arguments to find \$CHUTNEY_PATH" export TEST_NETWORK_RECURSING=true + # passing arguments to a sourced script only works in bash + # this will be fixed in #19699 . "$0" --dry-run "$@" # Call the chutney version of this script, if it exists, and we can find it if [ -d "$CHUTNEY_PATH" -a -x "$CHUTNEY_PATH/tools/test-network.sh" ]; then unset NETWORK_DRY_RUN - echo "$myname: Calling newer chutney script \ + $ECHO "$myname: Calling newer chutney script \ $CHUTNEY_PATH/tools/test-network.sh" "$CHUTNEY_PATH/tools/test-network.sh" "$@" exit $? else - echo "$myname: This script has moved to chutney/tools." - echo "$myname: Please update your chutney using 'git pull'." + $ECHO "$myname: This script has moved to chutney/tools." + $ECHO "$myname: Please update your chutney using 'git pull'." # When we switch to using test-network.sh in chutney/tools, we should # exit with a very loud failure here - echo "$myname: Falling back to the old tor version of the script." + $ECHO "$myname: Falling back to the old tor version of the script." fi fi @@ -89,10 +101,14 @@ do # process arguments, but don't call any other scripts export NETWORK_DRY_RUN=true ;; + --quiet) + export ECHO=true + export ECHO_N=true + ;; *) - echo "$myname: Sorry, I don't know what to do with '$1'." - echo "$myname: Maybe chutney's test-network.sh understands '$1'." - echo "$myname: Please update your chutney using 'git pull', and set \ + $ECHO "$myname: Sorry, I don't know what to do with '$1'." + $ECHO "$myname: Maybe chutney's test-network.sh understands '$1'." + $ECHO "$myname: Please update your chutney using 'git pull', and set \ \$CHUTNEY_PATH" # continue processing arguments during a dry run if [ "$NETWORK_DRY_RUN" != true ]; then @@ -113,15 +129,15 @@ if [ ! -d "$TOR_DIR" ]; then if [ -d "$BUILDDIR/src/or" -a -d "$BUILDDIR/src/tools" ]; then # Choose the build directory # But only if it looks like one - echo "$myname: \$TOR_DIR not set, trying \$BUILDDIR" + $ECHO "$myname: \$TOR_DIR not set, trying \$BUILDDIR" export TOR_DIR="$BUILDDIR" elif [ -d "$PWD/src/or" -a -d "$PWD/src/tools" ]; then # Guess the tor directory is the current directory # But only if it looks like one - echo "$myname: \$TOR_DIR not set, trying \$PWD" + $ECHO "$myname: \$TOR_DIR not set, trying \$PWD" export TOR_DIR="$PWD" else - echo "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries" + $ECHO "$myname: no \$TOR_DIR, chutney will use \$PATH for tor binaries" unset TOR_DIR fi fi @@ -133,19 +149,19 @@ fi # - fail and tell the user how to clone the chutney repository if [ ! -d "$CHUTNEY_PATH" -o ! -x "$CHUTNEY_PATH/chutney" ]; then if [ -x "$PWD/chutney" ]; then - echo "$myname: \$CHUTNEY_PATH not valid, trying \$PWD" + $ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$PWD" export CHUTNEY_PATH="$PWD" elif [ -d "$TOR_DIR" -a -d "$TOR_DIR/../chutney" -a \ -x "$TOR_DIR/../chutney/chutney" ]; then - echo "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney" + $ECHO "$myname: \$CHUTNEY_PATH not valid, trying \$TOR_DIR/../chutney" export CHUTNEY_PATH="$TOR_DIR/../chutney" else # TODO: work out how to package and install chutney, # so users can find it in $PATH - echo "$myname: missing 'chutney' in \$CHUTNEY_PATH ($CHUTNEY_PATH)" - echo "$myname: Get chutney: git clone https://git.torproject.org/\ + $ECHO "$myname: missing 'chutney' in \$CHUTNEY_PATH ($CHUTNEY_PATH)" + $ECHO "$myname: Get chutney: git clone https://git.torproject.org/\ chutney.git" - echo "$myname: Set \$CHUTNEY_PATH to a non-standard location: export \ + $ECHO "$myname: Set \$CHUTNEY_PATH to a non-standard location: export \ CHUTNEY_PATH=\`pwd\`/chutney" unset CHUTNEY_PATH exit 1 @@ -174,11 +190,13 @@ export CHUTNEY_NETWORK=networks/$NETWORK_FLAVOUR # And finish up if we're doing a dry run if [ "$NETWORK_DRY_RUN" = true ]; then # we can't exit here, it breaks argument processing - return + # this only works in bash: return semantics are shell-specific + # this will be fixed in #19699 + return 2>/dev/null || exit fi cd "$CHUTNEY_PATH" -./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 2 +./tools/bootstrap-network.sh $NETWORK_FLAVOUR || exit 3 # Sleep some, waiting for the network to bootstrap. # TODO: Add chutney command 'bootstrap-status' and use that instead. @@ -186,7 +204,7 @@ BOOTSTRAP_TIME=${BOOTSTRAP_TIME:-35} $ECHO_N "$myname: sleeping for $BOOTSTRAP_TIME seconds" n=$BOOTSTRAP_TIME; while [ $n -gt 0 ]; do sleep 1; n=$(expr $n - 1); $ECHO_N . -done; echo "" +done; $ECHO "" ./chutney verify $CHUTNEY_NETWORK VERIFY_EXIT_STATUS=$? # work around a bug/feature in make -j2 (or more) diff --git a/src/test/test.c b/src/test/test.c index 9a41b976b8..866408e856 100644 --- a/src/test/test.c +++ b/src/test/test.c @@ -1190,6 +1190,7 @@ struct testgroup_t testgroups[] = { { "checkdir/", checkdir_tests }, { "circuitlist/", circuitlist_tests }, { "circuitmux/", circuitmux_tests }, + { "circuituse/", circuituse_tests }, { "compat/libevent/", compat_libevent_tests }, { "config/", config_tests }, { "connection/", connection_tests }, @@ -1204,7 +1205,11 @@ struct testgroup_t testgroups[] = { { "entrynodes/", entrynodes_tests }, { "guardfraction/", guardfraction_tests }, { "extorport/", extorport_tests }, - { "hs/", hs_tests }, + { "legacy_hs/", hs_tests }, + { "hs_cache/", hs_cache }, + { "hs_descriptor/", hs_descriptor }, + { "hs_service/", hs_service_tests }, + { "hs_intropoint/", hs_intropoint_tests }, { "introduce/", introduce_tests }, { "keypin/", keypin_tests }, { "link-handshake/", link_handshake_tests }, diff --git a/src/test/test.h b/src/test/test.h index 25336ac83e..2bd58f51c8 100644 --- a/src/test/test.h +++ b/src/test/test.h @@ -75,6 +75,8 @@ const char *get_fname(const char *name); const char *get_fname_rnd(const char *name); struct crypto_pk_t *pk_generate(int idx); +void init_pregenerated_keys(void); +void free_pregenerated_keys(void); #define US2_CONCAT_2__(a, b) a ## __ ## b #define US_CONCAT_2__(a, b) a ## _ ## b @@ -183,6 +185,7 @@ extern struct testcase_t channeltls_tests[]; extern struct testcase_t checkdir_tests[]; extern struct testcase_t circuitlist_tests[]; extern struct testcase_t circuitmux_tests[]; +extern struct testcase_t circuituse_tests[]; extern struct testcase_t compat_libevent_tests[]; extern struct testcase_t config_tests[]; extern struct testcase_t connection_tests[]; @@ -197,6 +200,10 @@ extern struct testcase_t entrynodes_tests[]; extern struct testcase_t guardfraction_tests[]; extern struct testcase_t extorport_tests[]; extern struct testcase_t hs_tests[]; +extern struct testcase_t hs_cache[]; +extern struct testcase_t hs_descriptor[]; +extern struct testcase_t hs_service_tests[]; +extern struct testcase_t hs_intropoint_tests[]; extern struct testcase_t introduce_tests[]; extern struct testcase_t keypin_tests[]; extern struct testcase_t link_handshake_tests[]; diff --git a/src/test/test_bt_cl.c b/src/test/test_bt_cl.c index 95b4f48f11..709d599f52 100644 --- a/src/test/test_bt_cl.c +++ b/src/test/test_bt_cl.c @@ -19,14 +19,12 @@ static int crashtype = 0; #ifdef __GNUC__ #define NOINLINE __attribute__((noinline)) -#define NORETURN __attribute__((noreturn)) #endif int crash(int x) NOINLINE; int oh_what(int x) NOINLINE; int a_tangled_web(int x) NOINLINE; int we_weave(int x) NOINLINE; -static void abort_handler(int s) NORETURN; #ifdef HAVE_CFLAG_WNULL_DEREFERENCE DISABLE_GCC_WARNING(null-dereference) @@ -76,13 +74,6 @@ we_weave(int x) return a_tangled_web(x) + a_tangled_web(x+1); } -static void -abort_handler(int s) -{ - (void)s; - exit(0); -} - int main(int argc, char **argv) { @@ -120,8 +111,6 @@ main(int argc, char **argv) configure_backtrace_handler(NULL); - signal(SIGABRT, abort_handler); - printf("%d\n", we_weave(2)); clean_up_backtrace_handler(); diff --git a/src/test/test_buffers.c b/src/test/test_buffers.c index 3408da3aa9..9e7bdb8911 100644 --- a/src/test/test_buffers.c +++ b/src/test/test_buffers.c @@ -765,6 +765,49 @@ test_buffers_chunk_size(void *arg) ; } +static void +test_buffers_find_contentlen(void *arg) +{ + static const struct { + const char *headers; + int r; + int contentlen; + } results[] = { + { "Blah blah\r\nContent-Length: 1\r\n\r\n", 1, 1 }, + { "Blah blah\r\n\r\n", 0, 0 }, /* no content-len */ + { "Blah blah Content-Length: 1\r\n", 0, 0 }, /* no content-len. */ + { "Blah blah\r\nContent-Length: 100000\r\n", 1, 100000}, + { "Blah blah\r\nContent-Length: 1000000000000000000000000\r\n", -1, 0}, + { "Blah blah\r\nContent-Length: 0\r\n", 1, 0}, + { "Blah blah\r\nContent-Length: -1\r\n", -1, 0}, + { "Blah blah\r\nContent-Length: 1x\r\n", -1, 0}, + { "Blah blah\r\nContent-Length: 1 x\r\n", -1, 0}, + { "Blah blah\r\nContent-Length: 1 \r\n", 1, 1}, + { "Blah blah\r\nContent-Length: \r\n", -1, 0}, + { "Blah blah\r\nContent-Length: ", -1, 0}, + { "Blah blah\r\nContent-Length: 5050", -1, 0}, + { NULL, 0, 0 } + }; + int i; + + (void)arg; + + for (i = 0; results[i].headers; ++i) { + int r; + size_t sz; + size_t headerlen = strlen(results[i].headers); + char * tmp = tor_memdup(results[i].headers, headerlen);/* ensure no eos */ + sz = 999; /* to ensure it gets set */ + r = buf_http_find_content_length(tmp, headerlen, &sz); + tor_free(tmp); + log_debug(LD_DIR, "%d: %s", i, escaped(results[i].headers)); + tt_int_op(r, ==, results[i].r); + tt_int_op(sz, ==, results[i].contentlen); + } + done: + ; +} + struct testcase_t buffer_tests[] = { { "basic", test_buffers_basic, TT_FORK, NULL, NULL }, { "copy", test_buffer_copy, TT_FORK, NULL, NULL }, @@ -780,6 +823,7 @@ struct testcase_t buffer_tests[] = { { "tls_read_mocked", test_buffers_tls_read_mocked, 0, NULL, NULL }, { "chunk_size", test_buffers_chunk_size, 0, NULL, NULL }, + { "find_contentlen", test_buffers_find_contentlen, 0, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_cell_formats.c b/src/test/test_cell_formats.c index f839a5b72c..22c34b6d6c 100644 --- a/src/test/test_cell_formats.c +++ b/src/test/test_cell_formats.c @@ -11,6 +11,7 @@ #include "channel.h" #include "connection_edge.h" #include "connection_or.h" +#include "config.h" #include "onion.h" #include "onion_tap.h" #include "onion_fast.h" @@ -345,9 +346,9 @@ test_cfmt_connected_cells(void *arg) memset(&cell, 0, sizeof(cell)); tor_addr_parse(&addr, "30.40.50.60"); rh.length = connected_cell_format_payload(cell.payload+RELAY_HEADER_SIZE, - &addr, 128); + &addr, 1024); tt_int_op(rh.length, OP_EQ, 8); - test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000080"); + test_memeq_hex(cell.payload+RELAY_HEADER_SIZE, "1e28323c" "00000e10"); /* Try parsing it. */ tor_addr_make_unspec(&addr); @@ -355,7 +356,7 @@ test_cfmt_connected_cells(void *arg) tt_int_op(r, OP_EQ, 0); tt_int_op(tor_addr_family(&addr), OP_EQ, AF_INET); tt_str_op(fmt_addr(&addr), OP_EQ, "30.40.50.60"); - tt_int_op(ttl, OP_EQ, 128); + tt_int_op(ttl, OP_EQ, 3600); /* not 1024, since we clipped to 3600 */ /* Try an IPv6 address */ memset(&rh, 0, sizeof(rh)); @@ -698,6 +699,7 @@ test_cfmt_extend_cells(void *arg) tt_int_op(61681, OP_EQ, ec.orport_ipv4.port); tt_str_op("2002::f0:c51e", OP_EQ, fmt_addr(&ec.orport_ipv6.addr)); tt_int_op(4370, OP_EQ, ec.orport_ipv6.port); + tt_assert(ed25519_public_key_is_zero(&ec.ed_pubkey)); tt_mem_op(ec.node_id,OP_EQ, "anthropomorphization", 20); tt_int_op(cc->cell_type, OP_EQ, CELL_CREATE2); tt_int_op(cc->handshake_type, OP_EQ, 0x105); @@ -717,6 +719,37 @@ test_cfmt_extend_cells(void *arg) tt_mem_op(p2+1+8+22+4,OP_EQ, b, 99+20); tt_int_op(0, OP_EQ, create_cell_format_relayed(&cell, cc)); + /* Now let's add an ed25519 key to that extend2 cell. */ + memcpy(ec.ed_pubkey.pubkey, + "brownshoesdontmakeit/brownshoesd", 32); + + /* As before, since we aren't extending by ed25519. */ + get_options_mutable()->ExtendByEd25519ID = 0; + tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_len, OP_EQ, 89+99-34-20); + test_memeq_hex(p2, + "02000612F40001F0F1" + "0214616e7468726f706f6d6f727068697a6174696f6e" + "01050063"); + + /* Now try with the ed25519 ID. */ + get_options_mutable()->ExtendByEd25519ID = 1; + tt_int_op(0, OP_EQ, extend_cell_format(&p2_cmd, &p2_len, p2, &ec)); + tt_int_op(p2_len, OP_EQ, 89+99-34-20 + 34); + test_memeq_hex(p2, + "03000612F40001F0F1" + "0214616e7468726f706f6d6f727068697a6174696f6e" + // ed digest follows: + "0320" "62726f776e73686f6573646f6e746d616b656" + "9742f62726f776e73686f657364" + "01050063"); + /* Can we parse that? Did the key come through right? */ + memset(&ec, 0, sizeof(ec)); + tt_int_op(0, OP_EQ, extend_cell_parse(&ec, RELAY_COMMAND_EXTEND2, + p2, p2_len)); + tt_mem_op("brownshoesdontmakeit/brownshoesd", OP_EQ, + ec.ed_pubkey.pubkey, 32); + /* == Now try parsing some junk */ /* Try a too-long handshake */ @@ -1257,7 +1290,7 @@ struct testcase_t cell_format_tests[] = { TEST(connected_cells, 0), TEST(create_cells, 0), TEST(created_cells, 0), - TEST(extend_cells, 0), + TEST(extend_cells, TT_FORK), TEST(extended_cells, 0), TEST(resolved_cells, 0), TEST(is_destroy, 0), diff --git a/src/test/test_channel.c b/src/test/test_channel.c index a9e0634d9e..862bd6dfa6 100644 --- a/src/test/test_channel.c +++ b/src/test/test_channel.c @@ -1405,10 +1405,14 @@ test_channel_queue_impossible(void *arg) /* Let it drain and check that the bad entry is discarded */ test_chan_accept_cells = 1; + tor_capture_bugs_(1); channel_change_state(ch, CHANNEL_STATE_OPEN); tt_assert(test_cells_written == old_count); tt_int_op(chan_cell_queue_len(&(ch->outgoing_queue)), ==, 0); + tt_int_op(smartlist_len(tor_get_captured_bug_log_()), ==, 1); + tor_end_capture_bugs_(); + done: free_fake_channel(ch); @@ -1764,6 +1768,112 @@ test_channel_write(void *arg) return; } +static void +test_channel_id_map(void *arg) +{ + (void)arg; +#define N_CHAN 6 + char rsa_id[N_CHAN][DIGEST_LEN]; + ed25519_public_key_t *ed_id[N_CHAN]; + channel_t *chan[N_CHAN]; + int i; + ed25519_public_key_t ed_zero; + memset(&ed_zero, 0, sizeof(ed_zero)); + + tt_assert(sizeof(rsa_id[0]) == DIGEST_LEN); // Do I remember C? + + for (i = 0; i < N_CHAN; ++i) { + crypto_rand(rsa_id[i], DIGEST_LEN); + ed_id[i] = tor_malloc_zero(sizeof(*ed_id[i])); + crypto_rand((char*)ed_id[i]->pubkey, sizeof(ed_id[i]->pubkey)); + } + + /* For channel 3, have no Ed identity. */ + tor_free(ed_id[3]); + + /* Channel 2 and 4 have same ROSA identity */ + memcpy(rsa_id[4], rsa_id[2], DIGEST_LEN); + + /* Channel 2 and 4 and 5 have same RSA identity */ + memcpy(rsa_id[4], rsa_id[2], DIGEST_LEN); + memcpy(rsa_id[5], rsa_id[2], DIGEST_LEN); + + /* Channels 2 and 5 have same Ed25519 identity */ + memcpy(ed_id[5], ed_id[2], sizeof(*ed_id[2])); + + for (i = 0; i < N_CHAN; ++i) { + chan[i] = new_fake_channel(); + channel_register(chan[i]); + channel_set_identity_digest(chan[i], rsa_id[i], ed_id[i]); + } + + /* Lookup by RSA id only */ + tt_ptr_op(chan[0], OP_EQ, + channel_find_by_remote_identity(rsa_id[0], NULL)); + tt_ptr_op(chan[1], OP_EQ, + channel_find_by_remote_identity(rsa_id[1], NULL)); + tt_ptr_op(chan[3], OP_EQ, + channel_find_by_remote_identity(rsa_id[3], NULL)); + channel_t *ch; + ch = channel_find_by_remote_identity(rsa_id[2], NULL); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == NULL); + + /* As above, but with zero Ed25519 ID (meaning "any ID") */ + tt_ptr_op(chan[0], OP_EQ, + channel_find_by_remote_identity(rsa_id[0], &ed_zero)); + tt_ptr_op(chan[1], OP_EQ, + channel_find_by_remote_identity(rsa_id[1], &ed_zero)); + tt_ptr_op(chan[3], OP_EQ, + channel_find_by_remote_identity(rsa_id[3], &ed_zero)); + ch = channel_find_by_remote_identity(rsa_id[2], &ed_zero); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == chan[2] || ch == chan[4] || ch == chan[5]); + ch = channel_next_with_rsa_identity(ch); + tt_assert(ch == NULL); + + /* Lookup nonexistent RSA identity */ + tt_ptr_op(NULL, OP_EQ, + channel_find_by_remote_identity("!!!!!!!!!!!!!!!!!!!!", NULL)); + + /* Look up by full identity pair */ + tt_ptr_op(chan[0], OP_EQ, + channel_find_by_remote_identity(rsa_id[0], ed_id[0])); + tt_ptr_op(chan[1], OP_EQ, + channel_find_by_remote_identity(rsa_id[1], ed_id[1])); + tt_ptr_op(chan[3], OP_EQ, + channel_find_by_remote_identity(rsa_id[3], ed_id[3] /*NULL*/)); + tt_ptr_op(chan[4], OP_EQ, + channel_find_by_remote_identity(rsa_id[4], ed_id[4])); + ch = channel_find_by_remote_identity(rsa_id[2], ed_id[2]); + tt_assert(ch == chan[2] || ch == chan[5]); + + /* Look up RSA identity with wrong ed25519 identity */ + tt_ptr_op(NULL, OP_EQ, + channel_find_by_remote_identity(rsa_id[4], ed_id[0])); + tt_ptr_op(NULL, OP_EQ, + channel_find_by_remote_identity(rsa_id[2], ed_id[1])); + tt_ptr_op(NULL, OP_EQ, + channel_find_by_remote_identity(rsa_id[3], ed_id[1])); + + done: + for (i = 0; i < N_CHAN; ++i) { + channel_clear_identity_digest(chan[i]); + channel_unregister(chan[i]); + free_fake_channel(chan[i]); + tor_free(ed_id[i]); + } +#undef N_CHAN +} + struct testcase_t channel_tests[] = { { "dumpstats", test_channel_dumpstats, TT_FORK, NULL, NULL }, { "flush", test_channel_flush, TT_FORK, NULL, NULL }, @@ -1776,6 +1886,7 @@ struct testcase_t channel_tests[] = { { "queue_incoming", test_channel_queue_incoming, TT_FORK, NULL, NULL }, { "queue_size", test_channel_queue_size, TT_FORK, NULL, NULL }, { "write", test_channel_write, TT_FORK, NULL, NULL }, + { "id_map", test_channel_id_map, TT_FORK, NULL, NULL }, END_OF_TESTCASES }; diff --git a/src/test/test_channeltls.c b/src/test/test_channeltls.c index 08442e01b6..fd98ee40fb 100644 --- a/src/test/test_channeltls.c +++ b/src/test/test_channeltls.c @@ -32,6 +32,7 @@ static or_connection_t * tlschan_connection_or_connect_mock( const tor_addr_t *addr, uint16_t port, const char *digest, + const ed25519_public_key_t *ed_id, channel_tls_t *tlschan); static int tlschan_is_local_addr_mock(const tor_addr_t *addr); @@ -70,7 +71,7 @@ test_channeltls_create(void *arg) MOCK(connection_or_connect, tlschan_connection_or_connect_mock); /* Try connecting */ - ch = channel_tls_connect(&test_addr, 567, test_digest); + ch = channel_tls_connect(&test_addr, 567, test_digest, NULL); tt_assert(ch != NULL); done: @@ -119,7 +120,7 @@ test_channeltls_num_bytes_queued(void *arg) MOCK(connection_or_connect, tlschan_connection_or_connect_mock); /* Try connecting */ - ch = channel_tls_connect(&test_addr, 567, test_digest); + ch = channel_tls_connect(&test_addr, 567, test_digest, NULL); tt_assert(ch != NULL); /* @@ -204,7 +205,7 @@ test_channeltls_overhead_estimate(void *arg) MOCK(connection_or_connect, tlschan_connection_or_connect_mock); /* Try connecting */ - ch = channel_tls_connect(&test_addr, 567, test_digest); + ch = channel_tls_connect(&test_addr, 567, test_digest, NULL); tt_assert(ch != NULL); /* First case: silly low ratios should get clamped to 1.0 */ @@ -266,9 +267,11 @@ static or_connection_t * tlschan_connection_or_connect_mock(const tor_addr_t *addr, uint16_t port, const char *digest, + const ed25519_public_key_t *ed_id, channel_tls_t *tlschan) { or_connection_t *result = NULL; + (void) ed_id; // XXXX Not yet used. tt_assert(addr != NULL); tt_assert(port != 0); diff --git a/src/test/test_circuitlist.c b/src/test/test_circuitlist.c index e996c42115..7eed5fe225 100644 --- a/src/test/test_circuitlist.c +++ b/src/test/test_circuitlist.c @@ -4,10 +4,12 @@ #define TOR_CHANNEL_INTERNAL_ #define CIRCUITBUILD_PRIVATE #define CIRCUITLIST_PRIVATE +#define HS_CIRCUITMAP_PRIVATE #include "or.h" #include "channel.h" #include "circuitbuild.h" #include "circuitlist.h" +#include "hs_circuitmap.h" #include "test.h" #include "log_test_helpers.h" @@ -185,6 +187,9 @@ test_rend_token_maps(void *arg) (void)arg; (void)tok1; //xxxx + + hs_circuitmap_init(); + c1 = or_circuit_new(0, NULL); c2 = or_circuit_new(0, NULL); c3 = or_circuit_new(0, NULL); @@ -196,68 +201,68 @@ test_rend_token_maps(void *arg) tt_int_op(tok3[REND_TOKEN_LEN-1], OP_EQ, '.'); /* No maps; nothing there. */ - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok1)); - circuit_set_rendezvous_cookie(c1, tok1); - circuit_set_intro_point_digest(c2, tok2); + hs_circuitmap_register_rend_circ(c1, tok1); + hs_circuitmap_register_intro_circ_v2(c2, tok2); - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok3)); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok1)); /* Without purpose set, we don't get the circuits */ - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok2)); c1->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; c2->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; /* Okay, make sure they show up now. */ - tt_ptr_op(c1, OP_EQ, circuit_get_rendezvous(tok1)); - tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); + tt_ptr_op(c1, OP_EQ, hs_circuitmap_get_rend_circ(tok1)); + tt_ptr_op(c2, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok2)); /* Two items at the same place with the same token. */ c3->base_.purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; - circuit_set_rendezvous_cookie(c3, tok2); - tt_ptr_op(c2, OP_EQ, circuit_get_intro_point(tok2)); - tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); + hs_circuitmap_register_rend_circ(c3, tok2); + tt_ptr_op(c2, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ(tok2)); /* Marking a circuit makes it not get returned any more */ circuit_mark_for_close(TO_CIRCUIT(c1), END_CIRC_REASON_FINISHED); - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok1)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok1)); circuit_free(TO_CIRCUIT(c1)); c1 = NULL; /* Freeing a circuit makes it not get returned any more. */ circuit_free(TO_CIRCUIT(c2)); c2 = NULL; - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok2)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok2)); /* c3 -- are you still there? */ - tt_ptr_op(c3, OP_EQ, circuit_get_rendezvous(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_rend_circ(tok2)); /* Change its cookie. This never happens in Tor per se, but hey. */ c3->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; - circuit_set_intro_point_digest(c3, tok3); + hs_circuitmap_register_intro_circ_v2(c3, tok3); - tt_ptr_op(NULL, OP_EQ, circuit_get_rendezvous(tok2)); - tt_ptr_op(c3, OP_EQ, circuit_get_intro_point(tok3)); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_rend_circ(tok2)); + tt_ptr_op(c3, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok3)); /* Now replace c3 with c4. */ c4->base_.purpose = CIRCUIT_PURPOSE_INTRO_POINT; - circuit_set_intro_point_digest(c4, tok3); + hs_circuitmap_register_intro_circ_v2(c4, tok3); - tt_ptr_op(c4, OP_EQ, circuit_get_intro_point(tok3)); + tt_ptr_op(c4, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok3)); - tt_ptr_op(c3->rendinfo, OP_EQ, NULL); - tt_ptr_op(c4->rendinfo, OP_NE, NULL); - tt_mem_op(c4->rendinfo, OP_EQ, tok3, REND_TOKEN_LEN); + tt_ptr_op(c3->hs_token, OP_EQ, NULL); + tt_ptr_op(c4->hs_token, OP_NE, NULL); + tt_mem_op(c4->hs_token->token, OP_EQ, tok3, REND_TOKEN_LEN); /* Now clear c4's cookie. */ - circuit_set_intro_point_digest(c4, NULL); - tt_ptr_op(c4->rendinfo, OP_EQ, NULL); - tt_ptr_op(NULL, OP_EQ, circuit_get_intro_point(tok3)); + hs_circuitmap_remove_circuit(c4); + tt_ptr_op(c4->hs_token, OP_EQ, NULL); + tt_ptr_op(NULL, OP_EQ, hs_circuitmap_get_intro_circ_v2(tok3)); done: if (c1) diff --git a/src/test/test_circuituse.c b/src/test/test_circuituse.c new file mode 100644 index 0000000000..27a87660ff --- /dev/null +++ b/src/test/test_circuituse.c @@ -0,0 +1,304 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#define CIRCUITLIST_PRIVATE + +#include "or.h" +#include "test.h" +#include "test_helpers.h" +#include "config.h" +#include "circuitlist.h" +#include "circuituse.h" +#include "circuitbuild.h" +#include "nodelist.h" + +static void +test_circuit_is_available_for_use_ret_false_when_marked_for_close(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->marked_for_close = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_when_timestamp_dirty(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->timestamp_dirty = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_general_purpose(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_REND_POINT_WAITING; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_general_origin(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_for_non_origin_purpose(void *arg) +{ + (void)arg; + + circuit_t *circ = tor_malloc(sizeof(circuit_t)); + circ->purpose = CIRCUIT_PURPOSE_OR; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + tor_free(circ); +} + +static void +test_circuit_is_available_for_use_ret_false_unusable_for_new_conns(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + mark_circuit_unusable_for_new_conns(TO_ORIGIN_CIRCUIT(circ)); + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + circuit_free(circ); +} + +static void +test_circuit_is_available_for_use_returns_false_for_onehop_tunnel(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + oc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + oc->build_state->onehop_tunnel = 1; + + tt_int_op(0, ==, circuit_is_available_for_use(circ)); + + done: + circuit_free(circ); +} + +static void +test_circuit_is_available_for_use_returns_true_for_clean_circuit(void *arg) +{ + (void)arg; + + circuit_t *circ = dummy_origin_circuit_new(30); + origin_circuit_t *oc = TO_ORIGIN_CIRCUIT(circ); + oc->build_state = tor_malloc_zero(sizeof(cpath_build_state_t)); + oc->build_state->onehop_tunnel = 0; + + tt_int_op(1, ==, circuit_is_available_for_use(circ)); + + done: + circuit_free(circ); +} + +static int +mock_circuit_all_predicted_ports_handled(time_t now, + int *need_uptime, + int *need_capacity) +{ + (void)now; + + if (need_uptime && need_capacity) + return 0; + return 1; +} + +static consensus_path_type_t +mock_router_have_unknown_consensus_path(void) +{ + return CONSENSUS_PATH_UNKNOWN; +} + +static consensus_path_type_t +mock_router_have_exit_consensus_path(void) +{ + return CONSENSUS_PATH_EXIT; +} + +static void +test_needs_exit_circuits_ret_false_for_predicted_ports_and_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 0; + + time_t now = time(NULL); + tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); +} + +static void +test_needs_exit_circuits_ret_false_for_non_exit_consensus_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 1; + MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path); + + time_t now = time(NULL); + tt_int_op(0, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_exit_circuits_ret_true_for_predicted_ports_and_path(void *arg) +{ + (void)arg; + + MOCK(circuit_all_predicted_ports_handled, + mock_circuit_all_predicted_ports_handled); + int needs_uptime = 1; + int needs_capacity = 1; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + + time_t now = time(NULL); + tt_int_op(1, ==, needs_exit_circuits(now, &needs_uptime, &needs_capacity)); + + done: + UNMOCK(circuit_all_predicted_ports_handled); + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_circuits_for_build_ret_false_consensus_path_unknown(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_unknown_consensus_path); + tt_int_op(0, ==, needs_circuits_for_build(0)); + done: ; +} + +static void +test_needs_circuits_for_build_ret_false_if_num_less_than_max(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + tt_int_op(0, ==, needs_circuits_for_build(13)); + done: + UNMOCK(router_have_consensus_path); +} + +static void +test_needs_circuits_for_build_returns_true_when_more_are_needed(void *arg) +{ + (void)arg; + MOCK(router_have_consensus_path, mock_router_have_exit_consensus_path); + tt_int_op(1, ==, needs_circuits_for_build(0)); + done: + UNMOCK(router_have_consensus_path); +} + +struct testcase_t circuituse_tests[] = { + { "marked", + test_circuit_is_available_for_use_ret_false_when_marked_for_close, + TT_FORK, NULL, NULL + }, + { "timestamp", + test_circuit_is_available_for_use_ret_false_when_timestamp_dirty, + TT_FORK, NULL, NULL + }, + { "non_general", + test_circuit_is_available_for_use_ret_false_for_non_general_purpose, + TT_FORK, NULL, NULL + }, + { "non_general", + test_circuit_is_available_for_use_ret_false_for_non_general_origin, + TT_FORK, NULL, NULL + }, + { "origin", + test_circuit_is_available_for_use_ret_false_for_non_origin_purpose, + TT_FORK, NULL, NULL + }, + { "clean", + test_circuit_is_available_for_use_ret_false_unusable_for_new_conns, + TT_FORK, NULL, NULL + }, + { "onehop", + test_circuit_is_available_for_use_returns_false_for_onehop_tunnel, + TT_FORK, NULL, NULL + }, + { "clean_circ", + test_circuit_is_available_for_use_returns_true_for_clean_circuit, + TT_FORK, NULL, NULL + }, + { "exit_f", + test_needs_exit_circuits_ret_false_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "exit_t", + test_needs_exit_circuits_ret_true_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "non_exit", + test_needs_exit_circuits_ret_false_for_non_exit_consensus_path, + TT_FORK, NULL, NULL + }, + { "true", + test_needs_exit_circuits_ret_true_for_predicted_ports_and_path, + TT_FORK, NULL, NULL + }, + { "consensus_path_unknown", + test_needs_circuits_for_build_ret_false_consensus_path_unknown, + TT_FORK, NULL, NULL + }, + { "less_than_max", + test_needs_circuits_for_build_ret_false_if_num_less_than_max, + TT_FORK, NULL, NULL + }, + { "more_needed", + test_needs_circuits_for_build_returns_true_when_more_are_needed, + TT_FORK, NULL, NULL + }, + END_OF_TESTCASES +}; + diff --git a/src/test/test_config.c b/src/test/test_config.c index 2fc37b0bb8..3b19cad036 100644 --- a/src/test/test_config.c +++ b/src/test/test_config.c @@ -11,6 +11,7 @@ #include "or.h" #include "address.h" #include "addressmap.h" +#include "bridges.h" #include "circuitmux_ewma.h" #include "circuitbuild.h" #include "config.h" @@ -45,6 +46,8 @@ #include "transports.h" #include "util.h" +#include "test_helpers.h" + static void test_config_addressmap(void *arg) { @@ -4126,6 +4129,7 @@ test_config_parse_port_config__ports__ports_given(void *data) config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar NoIPv4Traffic " + "NoIPv6Traffic " "NoOnionTraffic"); ret = parse_port_config(NULL, config_port_invalid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, @@ -4147,6 +4151,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("DNSPort", "127.0.0.1:80 " + "NoIPv6Traffic " "NoIPv4Traffic NoOnionTraffic"); ret = parse_port_config(slout, config_port_valid, NULL, "DNS", CONN_TYPE_AP_DNS_LISTENER, NULL, 0, @@ -4162,6 +4167,7 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test failure if we have DNS but no ipv4 and no ipv6 config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("SOCKSPort", + "NoIPv6Traffic " "unix:/tmp/foo/bar NoIPv4Traffic"); ret = parse_port_config(NULL, config_port_invalid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, @@ -4174,6 +4180,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:/tmp/foo/bar " + "NoIPv6Traffic " "NoDNSRequest NoIPv4Traffic"); ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, @@ -4195,6 +4202,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar\" " + "NoIPv6Traffic " "NoDNSRequest NoIPv4Traffic"); ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, @@ -4216,6 +4224,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:\"/tmp/foo/ bar " + "NoIPv6Traffic " "NoDNSRequest NoIPv4Traffic"); ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, @@ -4227,6 +4236,7 @@ test_config_parse_port_config__ports__ports_given(void *data) SMARTLIST_FOREACH(slout,port_cfg_t *,pf,port_cfg_free(pf)); smartlist_clear(slout); config_port_valid = mock_config_line("SOCKSPort", "unix:\"\" " + "NoIPv6Traffic " "NoDNSRequest NoIPv4Traffic"); ret = parse_port_config(slout, config_port_valid, NULL, "SOCKS", CONN_TYPE_AP_LISTENER, NULL, 0, @@ -4601,7 +4611,7 @@ test_config_parse_port_config__ports__ports_given(void *data) tt_int_op(smartlist_len(slout), OP_EQ, 1); port_cfg = (port_cfg_t *)smartlist_get(slout, 0); tt_int_op(port_cfg->entry_cfg.ipv4_traffic, OP_EQ, 1); - tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 0); + tt_int_op(port_cfg->entry_cfg.ipv6_traffic, OP_EQ, 1); // Test failure for a SessionGroup argument with invalid value config_free_lines(config_port_invalid); config_port_invalid = NULL; @@ -4700,8 +4710,10 @@ test_config_parse_port_config__ports__ports_given(void *data) // Test failure when asked to parse an invalid address followed by auto config_free_lines(config_port_invalid); config_port_invalid = NULL; config_port_invalid = mock_config_line("DNSPort", "invalidstuff!!:auto"); + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); ret = parse_port_config(NULL, config_port_invalid, NULL, "DNS", 0, "127.0.0.46", 0, 0); + UNMOCK(tor_addr_lookup); tt_int_op(ret, OP_EQ, -1); // Test success with parsing both an address and a real port @@ -4890,6 +4902,33 @@ test_config_parse_port_config__ports__server_options(void *data) config_free_lines(config_port_valid); config_port_valid = NULL; } +static void +test_config_parse_log_severity(void *data) +{ + int ret; + const char *severity_log_lines[] = { + "debug file /tmp/debug.log", + "debug\tfile /tmp/debug.log", + "[handshake]debug [~net,~mm]info notice stdout", + "[handshake]debug\t[~net,~mm]info\tnotice\tstdout", + NULL + }; + int i; + log_severity_list_t *severity; + + (void) data; + + severity = tor_malloc(sizeof(log_severity_list_t)); + for (i = 0; severity_log_lines[i]; i++) { + memset(severity, 0, sizeof(log_severity_list_t)); + ret = parse_log_severity_config(&severity_log_lines[i], severity); + tt_int_op(ret, OP_EQ, 0); + } + + done: + tor_free(severity); +} + #define CONFIG_TEST(name, flags) \ { #name, test_config_ ## name, flags, NULL, NULL } @@ -4916,6 +4955,7 @@ struct testcase_t config_tests[] = { CONFIG_TEST(parse_port_config__ports__no_ports_given, 0), CONFIG_TEST(parse_port_config__ports__server_options, 0), CONFIG_TEST(parse_port_config__ports__ports_given, 0), + CONFIG_TEST(parse_log_severity, 0), END_OF_TESTCASES }; diff --git a/src/test/test_connection.c b/src/test/test_connection.c index d394fc9852..5cda4f3175 100644 --- a/src/test/test_connection.c +++ b/src/test/test_connection.c @@ -10,6 +10,7 @@ #include "test.h" #include "connection.h" +#include "hs_common.h" #include "main.h" #include "microdesc.h" #include "networkstatus.h" @@ -265,13 +266,9 @@ test_conn_get_rend_setup(const struct testcase_t *tc) rend_cache_init(); /* TODO: use directory_initiate_command_rend() to do this - maybe? */ - conn->rend_data = tor_malloc_zero(sizeof(rend_data_t)); tor_assert(strlen(TEST_CONN_REND_ADDR) == REND_SERVICE_ID_LEN_BASE32); - memcpy(conn->rend_data->onion_address, - TEST_CONN_REND_ADDR, - REND_SERVICE_ID_LEN_BASE32+1); - conn->rend_data->hsdirs_fp = smartlist_new(); - + conn->rend_data = rend_data_client_create(TEST_CONN_REND_ADDR, NULL, NULL, + REND_NO_AUTH); assert_connection_ok(&conn->base_, time(NULL)); return conn; @@ -551,7 +548,8 @@ test_conn_get_rend(void *arg) tt_assert(connection_get_by_type_state_rendquery( conn->base_.type, conn->base_.state, - conn->rend_data->onion_address) + rend_data_get_address( + conn->rend_data)) == TO_CONN(conn)); tt_assert(connection_get_by_type_state_rendquery( TEST_CONN_TYPE, diff --git a/src/test/test_containers.c b/src/test/test_containers.c index d8b82e0661..41f3f873de 100644 --- a/src/test/test_containers.c +++ b/src/test/test_containers.c @@ -501,13 +501,13 @@ test_container_smartlist_pos(void *arg) (void) arg; smartlist_t *sl = smartlist_new(); - smartlist_add(sl, tor_strdup("This")); - smartlist_add(sl, tor_strdup("is")); - smartlist_add(sl, tor_strdup("a")); - smartlist_add(sl, tor_strdup("test")); - smartlist_add(sl, tor_strdup("for")); - smartlist_add(sl, tor_strdup("a")); - smartlist_add(sl, tor_strdup("function")); + smartlist_add_strdup(sl, "This"); + smartlist_add_strdup(sl, "is"); + smartlist_add_strdup(sl, "a"); + smartlist_add_strdup(sl, "test"); + smartlist_add_strdup(sl, "for"); + smartlist_add_strdup(sl, "a"); + smartlist_add_strdup(sl, "function"); /* Test string_pos */ tt_int_op(smartlist_string_pos(NULL, "Fred"), ==, -1); @@ -830,7 +830,7 @@ test_container_strmap(void *arg) found_keys = smartlist_new(); while (!strmap_iter_done(iter)) { strmap_iter_get(iter,&k,&v); - smartlist_add(found_keys, tor_strdup(k)); + smartlist_add_strdup(found_keys, k); tt_ptr_op(v,OP_EQ, strmap_get(map, k)); if (!strcmp(k, "K2")) { @@ -882,6 +882,46 @@ test_container_strmap(void *arg) tor_free(v105); } +static void +test_container_smartlist_remove(void *arg) +{ + (void) arg; + int array[5]; + smartlist_t *sl = smartlist_new(); + int i,j; + + for (j=0; j < 2; ++j) + for (i=0; i < 5; ++i) + smartlist_add(sl, &array[i]); + + smartlist_remove(sl, &array[0]); + smartlist_remove(sl, &array[3]); + smartlist_remove(sl, &array[4]); + tt_assert(! smartlist_contains(sl, &array[0])); + tt_assert(smartlist_contains(sl, &array[1])); + tt_assert(smartlist_contains(sl, &array[2])); + tt_assert(! smartlist_contains(sl, &array[3])); + tt_assert(! smartlist_contains(sl, &array[4])); + tt_int_op(smartlist_len(sl), OP_EQ, 4); + + smartlist_clear(sl); + for (j=0; j < 2; ++j) + for (i=0; i < 5; ++i) + smartlist_add(sl, &array[i]); + + smartlist_remove_keeporder(sl, &array[0]); + smartlist_remove_keeporder(sl, &array[3]); + smartlist_remove_keeporder(sl, &array[4]); + tt_int_op(smartlist_len(sl), OP_EQ, 4); + tt_ptr_op(smartlist_get(sl, 0), OP_EQ, &array[1]); + tt_ptr_op(smartlist_get(sl, 1), OP_EQ, &array[2]); + tt_ptr_op(smartlist_get(sl, 2), OP_EQ, &array[1]); + tt_ptr_op(smartlist_get(sl, 3), OP_EQ, &array[2]); + + done: + smartlist_free(sl); +} + /** Run unit tests for getting the median of a list. */ static void test_container_order_functions(void *arg) @@ -1239,6 +1279,7 @@ struct testcase_t container_tests[] = { CONTAINER_LEGACY(smartlist_digests), CONTAINER_LEGACY(smartlist_join), CONTAINER_LEGACY(smartlist_pos), + CONTAINER(smartlist_remove, 0), CONTAINER(smartlist_ints_eq, 0), CONTAINER_LEGACY(bitarray), CONTAINER_LEGACY(digestset), diff --git a/src/test/test_controller.c b/src/test/test_controller.c index f19c846144..d9c0a1eaac 100644 --- a/src/test/test_controller.c +++ b/src/test/test_controller.c @@ -3,12 +3,14 @@ #define CONTROL_PRIVATE #include "or.h" +#include "bridges.h" #include "control.h" #include "entrynodes.h" #include "networkstatus.h" #include "rendservice.h" #include "routerlist.h" #include "test.h" +#include "test_helpers.h" static void test_add_onion_helper_keyarg(void *arg) @@ -185,8 +187,10 @@ test_rend_service_parse_port_config(void *arg) tor_free(err_msg); /* bogus IP address */ - cfg = rend_service_parse_port_config("100 1.2.3.4.5:9000", + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); + cfg = rend_service_parse_port_config("100 foo!!.example.com:9000", " ", &err_msg); + UNMOCK(tor_addr_lookup); tt_assert(!cfg); tt_str_op(err_msg, OP_EQ, "Unparseable address in hidden service port " "configuration."); diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c index 64a46f7914..d66ddccd4f 100644 --- a/src/test/test_crypto.c +++ b/src/test/test_crypto.c @@ -1135,6 +1135,54 @@ test_crypto_sha3_xof(void *arg) tor_free(mem_op_hex_tmp); } +/* Test our MAC-SHA3 function. There are not actually any MAC-SHA3 test + * vectors out there for our H(len(k) || k || m) construction. Hence what we + * are gonna do is test our crypto_mac_sha3_256() function against manually + * doing H(len(k) || k||m). If in the future the Keccak group decides to + * standarize an MAC construction and make test vectors, we should + * incorporate them here. */ +static void +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."; + + uint8_t hmac_test[DIGEST256_LEN]; + char hmac_manual[DIGEST256_LEN]; + + (void) arg; + + /* First let's use our nice HMAC-SHA3 function */ + crypto_mac_sha3_256(hmac_test, sizeof(hmac_test), + (uint8_t *) key, strlen(key), + (uint8_t *) msg, strlen(msg)); + + /* Now let's try a manual H(len(k) || k || m) construction */ + { + char *key_msg_concat = NULL, *all = NULL; + int result; + const uint64_t key_len_netorder = tor_htonll(strlen(key)); + size_t all_len; + + tor_asprintf(&key_msg_concat, "%s%s", key, msg); + all_len = sizeof(key_len_netorder) + strlen(key_msg_concat); + all = tor_malloc_zero(all_len); + memcpy(all, &key_len_netorder, sizeof(key_len_netorder)); + memcpy(all + sizeof(key_len_netorder), key_msg_concat, + strlen(key_msg_concat)); + + result = crypto_digest256(hmac_manual, all, all_len, DIGEST_SHA3_256); + tor_free(key_msg_concat); + tor_free(all); + tt_int_op(result, ==, 0); + } + + /* Now compare the two results */ + tt_mem_op(hmac_test, OP_EQ, hmac_manual, DIGEST256_LEN); + + done: ; +} + /** Run unit tests for our public key crypto functions */ static void test_crypto_pk(void *arg) @@ -2918,6 +2966,7 @@ struct testcase_t crypto_tests[] = { { "digest_names", test_crypto_digest_names, 0, NULL, NULL }, { "sha3", test_crypto_sha3, TT_FORK, NULL, NULL}, { "sha3_xof", test_crypto_sha3_xof, TT_FORK, NULL, NULL}, + { "mac_sha3", test_crypto_mac_sha3, TT_FORK, NULL, NULL}, CRYPTO_LEGACY(dh), { "aes_iv_AES", test_crypto_aes_iv, TT_FORK, &passthrough_setup, (void*)"aes" }, diff --git a/src/test/test_dir.c b/src/test/test_dir.c index cdc56acb89..4e5876fa3c 100644 --- a/src/test/test_dir.c +++ b/src/test/test_dir.c @@ -23,6 +23,7 @@ #include "directory.h" #include "dirserv.h" #include "dirvote.h" +#include "entrynodes.h" #include "hibernate.h" #include "memarea.h" #include "networkstatus.h" @@ -678,16 +679,16 @@ test_dir_parse_router_list(void *arg) routerinfo_t *ri = NULL; char d[DIGEST_LEN]; - smartlist_add(chunks, tor_strdup(EX_RI_MINIMAL)); // ri 0 - smartlist_add(chunks, tor_strdup(EX_RI_BAD_PORTS)); // bad ri 0 - smartlist_add(chunks, tor_strdup(EX_EI_MAXIMAL)); // ei 0 - smartlist_add(chunks, tor_strdup(EX_EI_BAD_SIG2)); // bad ei -- - smartlist_add(chunks, tor_strdup(EX_EI_BAD_NICKNAME));// bad ei 0 - smartlist_add(chunks, tor_strdup(EX_RI_BAD_SIG1)); // bad ri -- - smartlist_add(chunks, tor_strdup(EX_EI_BAD_PUBLISHED)); // bad ei 1 - smartlist_add(chunks, tor_strdup(EX_RI_MAXIMAL)); // ri 1 - smartlist_add(chunks, tor_strdup(EX_RI_BAD_FAMILY)); // bad ri 1 - smartlist_add(chunks, tor_strdup(EX_EI_MINIMAL)); // ei 1 + smartlist_add_strdup(chunks, EX_RI_MINIMAL); // ri 0 + smartlist_add_strdup(chunks, EX_RI_BAD_PORTS); // bad ri 0 + smartlist_add_strdup(chunks, EX_EI_MAXIMAL); // ei 0 + smartlist_add_strdup(chunks, EX_EI_BAD_SIG2); // bad ei -- + smartlist_add_strdup(chunks, EX_EI_BAD_NICKNAME);// bad ei 0 + smartlist_add_strdup(chunks, EX_RI_BAD_SIG1); // bad ri -- + smartlist_add_strdup(chunks, EX_EI_BAD_PUBLISHED); // bad ei 1 + smartlist_add_strdup(chunks, EX_RI_MAXIMAL); // ri 1 + smartlist_add_strdup(chunks, EX_RI_BAD_FAMILY); // bad ri 1 + smartlist_add_strdup(chunks, EX_EI_MINIMAL); // ei 1 list = smartlist_join_strings(chunks, "", 0, NULL); @@ -812,19 +813,19 @@ test_dir_load_routers(void *arg) #define ADD(str) \ do { \ tt_int_op(0,OP_EQ,router_get_router_hash(str, strlen(str), buf)); \ - smartlist_add(wanted, tor_strdup(hex_str(buf, DIGEST_LEN))); \ + smartlist_add_strdup(wanted, hex_str(buf, DIGEST_LEN)); \ } while (0) MOCK(router_get_dl_status_by_descriptor_digest, mock_router_get_dl_status); update_approx_time(1412510400); - smartlist_add(chunks, tor_strdup(EX_RI_MINIMAL)); - smartlist_add(chunks, tor_strdup(EX_RI_BAD_FINGERPRINT)); - smartlist_add(chunks, tor_strdup(EX_RI_BAD_SIG2)); - smartlist_add(chunks, tor_strdup(EX_RI_MAXIMAL)); - smartlist_add(chunks, tor_strdup(EX_RI_BAD_PORTS)); - smartlist_add(chunks, tor_strdup(EX_RI_BAD_TOKENS)); + smartlist_add_strdup(chunks, EX_RI_MINIMAL); + smartlist_add_strdup(chunks, EX_RI_BAD_FINGERPRINT); + smartlist_add_strdup(chunks, EX_RI_BAD_SIG2); + smartlist_add_strdup(chunks, EX_RI_MAXIMAL); + smartlist_add_strdup(chunks, EX_RI_BAD_PORTS); + smartlist_add_strdup(chunks, EX_RI_BAD_TOKENS); /* not ADDing MINIMIAL */ ADD(EX_RI_MAXIMAL); @@ -932,18 +933,18 @@ test_dir_load_extrainfo(void *arg) #define ADD(str) \ do { \ tt_int_op(0,OP_EQ,router_get_extrainfo_hash(str, strlen(str), buf)); \ - smartlist_add(wanted, tor_strdup(hex_str(buf, DIGEST_LEN))); \ + smartlist_add_strdup(wanted, hex_str(buf, DIGEST_LEN)); \ } while (0) mock_ei_insert_list = smartlist_new(); MOCK(router_get_by_extrainfo_digest, mock_get_by_ei_desc_digest); MOCK(extrainfo_insert, mock_ei_insert); - smartlist_add(chunks, tor_strdup(EX_EI_MINIMAL)); - smartlist_add(chunks, tor_strdup(EX_EI_BAD_NICKNAME)); - smartlist_add(chunks, tor_strdup(EX_EI_MAXIMAL)); - smartlist_add(chunks, tor_strdup(EX_EI_BAD_PUBLISHED)); - smartlist_add(chunks, tor_strdup(EX_EI_BAD_TOKENS)); + smartlist_add_strdup(chunks, EX_EI_MINIMAL); + smartlist_add_strdup(chunks, EX_EI_BAD_NICKNAME); + smartlist_add_strdup(chunks, EX_EI_MAXIMAL); + smartlist_add_strdup(chunks, EX_EI_BAD_PUBLISHED); + smartlist_add_strdup(chunks, EX_EI_BAD_TOKENS); /* not ADDing MINIMIAL */ ADD(EX_EI_MAXIMAL); @@ -1493,6 +1494,15 @@ test_dir_param_voting(void *arg) tt_int_op(-8,OP_EQ, networkstatus_get_param(&vote4, "ab", -12, -100, -8)); tt_int_op(0,OP_EQ, networkstatus_get_param(&vote4, "foobar", 0, -100, 8)); + tt_int_op(100,OP_EQ, networkstatus_get_overridable_param( + &vote4, -1, "x-yz", 50, 0, 300)); + tt_int_op(30,OP_EQ, networkstatus_get_overridable_param( + &vote4, 30, "x-yz", 50, 0, 300)); + tt_int_op(0,OP_EQ, networkstatus_get_overridable_param( + &vote4, -101, "foobar", 0, -100, 8)); + tt_int_op(-99,OP_EQ, networkstatus_get_overridable_param( + &vote4, -99, "foobar", 0, -100, 8)); + smartlist_add(votes, &vote1); /* Do the first tests without adding all the other votes, for @@ -1874,6 +1884,249 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now) return; } +static void +test_dir_networkstatus_compute_bw_weights_v10(void *arg) +{ + (void) arg; + smartlist_t *chunks = smartlist_new(); + int64_t G, M, E, D, T, weight_scale; + int ret; + weight_scale = 10000; + + /* no case. one or more of the values is 0 */ + G = M = E = D = 0; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 0); + tt_int_op(smartlist_len(chunks), OP_EQ, 0); + + /* case 1 */ + /* XXX dir-spec not followed? See #20272. If it isn't closed, then this is + * testing current behavior, not spec. */ + G = E = 10; + M = D = 1; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_int_op(smartlist_len(chunks), OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=3333 " + "Wbe=3000 Wbg=3000 Wbm=10000 Wdb=10000 Web=10000 Wed=3333 Wee=7000 " + "Weg=3333 Wem=7000 Wgb=10000 Wgd=3333 Wgg=7000 Wgm=7000 Wmb=10000 " + "Wmd=3333 Wme=3000 Wmg=3000 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2a E scarce */ + M = 100; + G = 20; + E = D = 5; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 " + "Wem=10000 Wgb=10000 Wgd=0 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=0 Wme=0 " + "Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2a G scarce */ + M = 100; + E = 20; + G = D = 5; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=0 Wee=10000 Weg=0 Wem=10000 " + "Wgb=10000 Wgd=10000 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=0 Wme=0 Wmg=0 " + "Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2b1 (Wgg=1, Wmd=Wgd) */ + M = 10; + E = 30; + G = 10; + D = 100; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=4000 " + "Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=2000 Wee=10000 Weg=2000 " + "Wem=10000 Wgb=10000 Wgd=4000 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=4000 " + "Wme=0 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2b2 */ + M = 60; + E = 30; + G = 10; + D = 100; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=666 Wbe=0 " + "Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=3666 Wee=10000 Weg=3666 " + "Wem=10000 Wgb=10000 Wgd=5668 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=666 " + "Wme=0 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 2b3 */ + /* XXX I can't get a combination of values that hits this case without error, + * so this just tests that it fails. See #20285. Also see #20284 as 2b3 does + * not follow dir-spec. */ + /* (E < T/3 && G < T/3) && (E+D>=G || G+D>=E) && (M > T/3) */ + M = 80; + E = 30; + G = 30; + D = 30; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 0); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 3a G scarce */ + M = 10; + E = 30; + G = 10; + D = 5; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 " + "Wbe=3333 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=0 Wee=6667 Weg=0 " + "Wem=6667 Wgb=10000 Wgd=10000 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=0 " + "Wme=3333 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 3a E scarce */ + M = 10; + E = 10; + G = 30; + D = 5; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=3333 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 " + "Wem=10000 Wgb=10000 Wgd=0 Wgg=6667 Wgm=6667 Wmb=10000 Wmd=0 Wme=0 " + "Wmg=3333 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 3bg */ + M = 10; + E = 30; + G = 10; + D = 10; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 " + "Wbe=3334 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=0 Wee=6666 Weg=0 " + "Wem=6666 Wgb=10000 Wgd=10000 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=0 " + "Wme=3334 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case 3be */ + M = 10; + E = 10; + G = 30; + D = 10; + T = G + M + E + D; + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=3334 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 " + "Wem=10000 Wgb=10000 Wgd=0 Wgg=6666 Wgm=6666 Wmb=10000 Wmd=0 Wme=0 " + "Wmg=3334 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case from 21 Jul 2013 (3be) */ + G = 5483409; + M = 1455379; + E = 980834; + D = 3385803; + T = 11305425; + tt_i64_op(G+M+E+D, OP_EQ, T); + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=883 Wbe=0 " + "Wbg=3673 Wbm=10000 Wdb=10000 Web=10000 Wed=8233 Wee=10000 Weg=8233 " + "Wem=10000 Wgb=10000 Wgd=883 Wgg=6327 Wgm=6327 Wmb=10000 Wmd=883 Wme=0 " + "Wmg=3673 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case from 04 Oct 2016 (3a E scarce) */ + G=29322240; + M=4721546; + E=1522058; + D=9273571; + T=44839415; + tt_i64_op(G+M+E+D, OP_EQ, T); + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=0 Wbe=0 " + "Wbg=4194 Wbm=10000 Wdb=10000 Web=10000 Wed=10000 Wee=10000 Weg=10000 " + "Wem=10000 Wgb=10000 Wgd=0 Wgg=5806 Wgm=5806 Wmb=10000 Wmd=0 Wme=0 " + "Wmg=4194 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* case from 04 Sep 2013 (2b1) */ + G=3091352; + M=1838837; + E=2109300; + D=2469369; + T=9508858; + tt_i64_op(G+M+E+D, OP_EQ, T); + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=317 " + "Wbe=5938 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=9366 Wee=4061 " + "Weg=9366 Wem=4061 Wgb=10000 Wgd=317 Wgg=10000 Wgm=10000 Wmb=10000 " + "Wmd=317 Wme=5938 Wmg=0 Wmm=10000\n"); + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_clear(chunks); + + /* explicitly test initializing weights to 1*/ + G=1; + M=1; + E=1; + D=1; + T=4; + tt_i64_op(G+M+E+D, OP_EQ, T); + ret = networkstatus_compute_bw_weights_v10(chunks, G, M, E, D, T, + weight_scale); + tt_str_op(smartlist_get(chunks, 0), OP_EQ, "bandwidth-weights Wbd=3333 " + "Wbe=0 Wbg=0 Wbm=10000 Wdb=10000 Web=10000 Wed=3333 Wee=10000 Weg=3333 " + "Wem=10000 Wgb=10000 Wgd=3333 Wgg=10000 Wgm=10000 Wmb=10000 Wmd=3333 " + "Wme=0 Wmg=0 Wmm=10000\n"); + + done: + SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); + smartlist_free(chunks); +} + static authority_cert_t *mock_cert; static authority_cert_t * @@ -3253,17 +3506,88 @@ test_dir_http_handling(void *args) } static void -test_dir_purpose_needs_anonymity(void *arg) +test_dir_purpose_needs_anonymity_returns_true_by_default(void *arg) +{ + (void)arg; + + tor_capture_bugs_(1); + setup_full_capture_of_logs(LOG_WARN); + tt_int_op(1, ==, purpose_needs_anonymity(0, 0, NULL)); + tt_int_op(1, ==, smartlist_len(tor_get_captured_bug_log_())); + expect_single_log_msg_containing("Called with dir_purpose=0"); + + tor_end_capture_bugs_(); + done: + tor_end_capture_bugs_(); + teardown_capture_of_logs(); +} + +static void +test_dir_purpose_needs_anonymity_returns_true_for_bridges(void *arg) { (void)arg; - tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE)); - tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_GENERAL)); - tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_FETCH_MICRODESC, - ROUTER_PURPOSE_GENERAL)); + + tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE, NULL)); + tt_int_op(1, ==, purpose_needs_anonymity(0, ROUTER_PURPOSE_BRIDGE, + "foobar")); + tt_int_op(1, ==, purpose_needs_anonymity(DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2, + ROUTER_PURPOSE_BRIDGE, NULL)); done: ; } static void +test_dir_purpose_needs_anonymity_returns_false_for_own_bridge_desc(void *arg) +{ + (void)arg; + tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_FETCH_SERVERDESC, + ROUTER_PURPOSE_BRIDGE, + "authority.z")); + done: ; +} + +static void +test_dir_purpose_needs_anonymity_returns_true_for_sensitive_purpose(void *arg) +{ + (void)arg; + + tt_int_op(1, ==, purpose_needs_anonymity( + DIR_PURPOSE_HAS_FETCHED_RENDDESC_V2, + ROUTER_PURPOSE_GENERAL, NULL)); + tt_int_op(1, ==, purpose_needs_anonymity( + DIR_PURPOSE_UPLOAD_RENDDESC_V2, 0, NULL)); + tt_int_op(1, ==, purpose_needs_anonymity( + DIR_PURPOSE_FETCH_RENDDESC_V2, 0, NULL)); + done: ; +} + +static void +test_dir_purpose_needs_anonymity_ret_false_for_non_sensitive_conn(void *arg) +{ + (void)arg; + + tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_DIR, + ROUTER_PURPOSE_GENERAL, NULL)); + tt_int_op(0, ==, purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_VOTE, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_UPLOAD_SIGNATURES, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_STATUS_VOTE, 0, NULL)); + tt_int_op(0, ==, purpose_needs_anonymity( + DIR_PURPOSE_FETCH_DETACHED_SIGNATURES, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_CONSENSUS, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_CERTIFICATE, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_SERVERDESC, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_EXTRAINFO, 0, NULL)); + tt_int_op(0, ==, + purpose_needs_anonymity(DIR_PURPOSE_FETCH_MICRODESC, 0, NULL)); + done: ; +} + +static void test_dir_fetch_type(void *arg) { (void)arg; @@ -4035,7 +4359,6 @@ test_dir_should_use_directory_guards(void *data) tt_int_op(should_use_directory_guards(options), OP_EQ, 0); tt_int_op(CALLED(public_server_mode), OP_EQ, 1); - options->UseEntryGuardsAsDirGuards = 1; options->UseEntryGuards = 1; options->DownloadExtraInfo = 0; options->FetchDirInfoEarly = 0; @@ -4049,29 +4372,24 @@ test_dir_should_use_directory_guards(void *data) tt_int_op(CALLED(public_server_mode), OP_EQ, 3); options->UseEntryGuards = 1; - options->UseEntryGuardsAsDirGuards = 0; - tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 4); - options->UseEntryGuardsAsDirGuards = 1; - options->DownloadExtraInfo = 1; tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 5); + tt_int_op(CALLED(public_server_mode), OP_EQ, 4); options->DownloadExtraInfo = 0; options->FetchDirInfoEarly = 1; tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 6); + tt_int_op(CALLED(public_server_mode), OP_EQ, 5); options->FetchDirInfoEarly = 0; options->FetchDirInfoExtraEarly = 1; tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 7); + tt_int_op(CALLED(public_server_mode), OP_EQ, 6); options->FetchDirInfoExtraEarly = 0; options->FetchUselessDescriptors = 1; tt_int_op(should_use_directory_guards(options), OP_EQ, 0); - tt_int_op(CALLED(public_server_mode), OP_EQ, 8); + tt_int_op(CALLED(public_server_mode), OP_EQ, 7); options->FetchUselessDescriptors = 0; done: @@ -4088,7 +4406,8 @@ directory_initiate_command_routerstatus, (const routerstatus_t *status, const char *resource, const char *payload, size_t payload_len, - time_t if_modified_since)); + time_t if_modified_since, + circuit_guard_state_t *guardstate)); static void test_dir_should_not_init_request_to_ourselves(void *data) @@ -4195,7 +4514,8 @@ NS(directory_initiate_command_routerstatus)(const routerstatus_t *status, const char *resource, const char *payload, size_t payload_len, - time_t if_modified_since) + time_t if_modified_since, + circuit_guard_state_t *guardstate) { (void)status; (void)dir_purpose; @@ -4205,6 +4525,7 @@ NS(directory_initiate_command_routerstatus)(const routerstatus_t *status, (void)payload; (void)payload_len; (void)if_modified_since; + (void)guardstate; CALLED(directory_initiate_command_routerstatus)++; } @@ -5148,9 +5469,9 @@ listdir_mock(const char *dname) (void)dname; l = smartlist_new(); - smartlist_add(l, tor_strdup("foo")); - smartlist_add(l, tor_strdup("bar")); - smartlist_add(l, tor_strdup("baz")); + smartlist_add_strdup(l, "foo"); + smartlist_add_strdup(l, "bar"); + smartlist_add_strdup(l, "baz"); return l; } @@ -5437,6 +5758,67 @@ test_dir_assumed_flags(void *arg) routerstatus_free(rs); } +static void +test_dir_post_parsing(void *arg) +{ + (void) arg; + + /* Test the version parsing from an HS descriptor publish request. */ + { + const char *end; + const char *prefix = "/tor/hs/"; + int version = parse_hs_version_from_post("/tor/hs//publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/a/publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish", prefix, &end); + tt_int_op(version, OP_EQ, 3); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("/tor/hs/42/publish", prefix, &end); + tt_int_op(version, OP_EQ, 42); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("/tor/hs/18163/publish",prefix, &end); + tt_int_op(version, OP_EQ, 18163); + tt_str_op(end, OP_EQ, "/publish"); + version = parse_hs_version_from_post("JUNKJUNKJUNK", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish", "blah", &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + /* Missing the '/' at the end of the prefix. */ + version = parse_hs_version_from_post("/tor/hs/3/publish", "/tor/hs", &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/random/blah/tor/hs/3/publish", + prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + version = parse_hs_version_from_post("/tor/hs/3/publish/random/junk", + prefix, &end); + tt_int_op(version, OP_EQ, 3); + tt_str_op(end, OP_EQ, "/publish/random/junk"); + version = parse_hs_version_from_post("/tor/hs/-1/publish", prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + /* INT_MAX */ + version = parse_hs_version_from_post("/tor/hs/2147483647/publish", + prefix, &end); + tt_int_op(version, OP_EQ, INT_MAX); + tt_str_op(end, OP_EQ, "/publish"); + /* INT_MAX + 1*/ + version = parse_hs_version_from_post("/tor/hs/2147483648/publish", + prefix, &end); + tt_int_op(version, OP_EQ, -1); + tt_ptr_op(end, OP_EQ, NULL); + } + + done: + ; +} + #define DIR_LEGACY(name) \ { #name, test_dir_ ## name , TT_FORK, NULL, NULL } @@ -5470,7 +5852,12 @@ struct testcase_t dir_tests[] = { DIR(fmt_control_ns, 0), DIR(dirserv_set_routerstatus_testing, 0), DIR(http_handling, 0), - DIR(purpose_needs_anonymity, 0), + DIR(purpose_needs_anonymity_returns_true_for_bridges, 0), + DIR(purpose_needs_anonymity_returns_false_for_own_bridge_desc, 0), + DIR(purpose_needs_anonymity_returns_true_by_default, 0), + DIR(purpose_needs_anonymity_returns_true_for_sensitive_purpose, 0), + DIR(purpose_needs_anonymity_ret_false_for_non_sensitive_conn, 0), + DIR(post_parsing, 0), DIR(fetch_type, 0), DIR(packages, 0), DIR(download_status_schedule, 0), @@ -5491,6 +5878,7 @@ struct testcase_t dir_tests[] = { DIR_ARG(find_dl_schedule, TT_FORK, "cf"), DIR_ARG(find_dl_schedule, TT_FORK, "ca"), DIR(assumed_flags, 0), + DIR(networkstatus_compute_bw_weights_v10, 0), END_OF_TESTCASES }; diff --git a/src/test/test_dir_handle_get.c b/src/test/test_dir_handle_get.c index a0f22f1f0c..a0868f9253 100644 --- a/src/test/test_dir_handle_get.c +++ b/src/test/test_dir_handle_get.c @@ -30,6 +30,7 @@ #include "dirserv.h" #include "torgzip.h" #include "dirvote.h" +#include "log_test_helpers.h" #ifdef _WIN32 /* For mkdir() */ @@ -50,22 +51,10 @@ ENABLE_GCC_WARNING(overlength-strings) #define NS_MODULE dir_handle_get -static void -connection_write_to_buf_mock(const char *string, size_t len, - connection_t *conn, int zlib) -{ - (void) zlib; - - tor_assert(string); - tor_assert(conn); - - write_to_buf(string, len, conn->outbuf); -} - -#define GET(path) "GET " path " HTTP/1.0\r\n\r\n" #define NOT_FOUND "HTTP/1.0 404 Not found\r\n\r\n" #define BAD_REQUEST "HTTP/1.0 400 Bad request\r\n\r\n" #define SERVER_BUSY "HTTP/1.0 503 Directory busy, try again later\r\n\r\n" +#define TOO_OLD "HTTP/1.0 404 Consensus is too old\r\n\r\n" #define NOT_ENOUGH_CONSENSUS_SIGNATURES "HTTP/1.0 404 " \ "Consensus not signed by sufficient number of requested authorities\r\n\r\n" @@ -1630,6 +1619,7 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_enough_sigs(void* d) mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); mock_ns_val->flavor = FLAV_NS; mock_ns_val->voters = smartlist_new(); + mock_ns_val->valid_until = time(NULL); /* init mock */ init_mock_options(); @@ -1709,6 +1699,62 @@ test_dir_handle_get_status_vote_current_consensus_ns_not_found(void* data) or_options_free(mock_options); mock_options = NULL; } +static void +test_dir_handle_get_status_vote_current_consensus_too_old(void *data) +{ + dir_connection_t *conn = NULL; + char *header = NULL; + (void)data; + + mock_ns_val = tor_malloc_zero(sizeof(networkstatus_t)); + mock_ns_val->flavor = FLAV_MICRODESC; + mock_ns_val->valid_until = time(NULL) - (60 * 60 * 24) - 1; + + init_mock_options(); + MOCK(get_options, mock_get_options); + MOCK(connection_write_to_buf_impl_, connection_write_to_buf_mock); + MOCK(networkstatus_get_latest_consensus_by_flavor, mock_ns_get_by_flavor); + + conn = new_dir_conn(); + + setup_capture_of_logs(LOG_WARN); + + tt_int_op(0, OP_EQ, directory_handle_command_get(conn, + GET("/tor/status-vote/current/consensus-microdesc"), NULL, 0)); + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, + NULL, NULL, 1, 0); + tt_assert(header); + tt_str_op(TOO_OLD, OP_EQ, header); + + expect_log_msg_containing("too old"); + + tor_free(header); + teardown_capture_of_logs(); + + setup_capture_of_logs(LOG_WARN); + + tt_int_op(0, OP_EQ, directory_handle_command_get(conn, + GET("/tor/status-vote/current/consensus"), NULL, 0)); + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &header, MAX_HEADERS_SIZE, + NULL, NULL, 1, 0); + tt_assert(header); + tt_str_op(TOO_OLD, OP_EQ, header); + + expect_no_log_entry(); + + done: + teardown_capture_of_logs(); + UNMOCK(networkstatus_get_latest_consensus_by_flavor); + UNMOCK(connection_write_to_buf_impl_); + UNMOCK(get_options); + connection_free_(TO_CONN(conn)); + tor_free(header); + tor_free(mock_ns_val); + or_options_free(mock_options); mock_options = NULL; +} + NS_DECL(int, geoip_get_country_by_addr, (const tor_addr_t *addr)); int @@ -2494,6 +2540,7 @@ struct testcase_t dir_handle_get_tests[] = { DIR_HANDLE_CMD(status_vote_next_authority, 0), DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_enough_sigs, 0), DIR_HANDLE_CMD(status_vote_current_consensus_ns_not_found, 0), + DIR_HANDLE_CMD(status_vote_current_consensus_too_old, 0), DIR_HANDLE_CMD(status_vote_current_consensus_ns_busy, 0), DIR_HANDLE_CMD(status_vote_current_consensus_ns, 0), DIR_HANDLE_CMD(status_vote_current_d_not_found, 0), diff --git a/src/test/test_dns.c b/src/test/test_dns.c index 8346c0a33f..6a8e92cb47 100644 --- a/src/test/test_dns.c +++ b/src/test/test_dns.c @@ -16,30 +16,11 @@ NS(test_main)(void *arg) { (void)arg; - uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_TTL / 2; + uint32_t ttl_mid = MIN_DNS_TTL_AT_EXIT / 2 + MAX_DNS_TTL_AT_EXIT / 2; - tt_int_op(dns_clip_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL); - tt_int_op(dns_clip_ttl(ttl_mid),==,ttl_mid); - tt_int_op(dns_clip_ttl(MAX_DNS_TTL + 1),==,MAX_DNS_TTL); - - done: - return; -} - -#undef NS_SUBMODULE - -#define NS_SUBMODULE expiry_ttl - -static void -NS(test_main)(void *arg) -{ - (void)arg; - - uint32_t ttl_mid = MIN_DNS_TTL / 2 + MAX_DNS_ENTRY_AGE / 2; - - tt_int_op(dns_get_expiry_ttl(MIN_DNS_TTL - 1),==,MIN_DNS_TTL); - tt_int_op(dns_get_expiry_ttl(ttl_mid),==,ttl_mid); - tt_int_op(dns_get_expiry_ttl(MAX_DNS_ENTRY_AGE + 1),==,MAX_DNS_ENTRY_AGE); + tt_int_op(dns_clip_ttl(MIN_DNS_TTL_AT_EXIT - 1),==,MIN_DNS_TTL_AT_EXIT); + tt_int_op(dns_clip_ttl(ttl_mid),==,MAX_DNS_TTL_AT_EXIT); + tt_int_op(dns_clip_ttl(MAX_DNS_TTL_AT_EXIT + 1),==,MAX_DNS_TTL_AT_EXIT); done: return; @@ -749,7 +730,6 @@ NS(test_main)(void *arg) struct testcase_t dns_tests[] = { TEST_CASE(clip_ttl), - TEST_CASE(expiry_ttl), TEST_CASE(resolve), TEST_CASE_ASPECT(resolve_impl, addr_is_ip_no_need_to_resolve), TEST_CASE_ASPECT(resolve_impl, non_exit), diff --git a/src/test/test_entryconn.c b/src/test/test_entryconn.c index 9580a1fd3f..50848cfec2 100644 --- a/src/test/test_entryconn.c +++ b/src/test/test_entryconn.c @@ -100,7 +100,7 @@ test_entryconn_rewrite_automap_ipv4(void *arg) ec3 = entry_connection_new(CONN_TYPE_AP, AF_INET); get_options_mutable()->AutomapHostsOnResolve = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, tor_strdup(".")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, "."); parse_virtual_addr_network("127.202.0.0/16", AF_INET, 0, &msg); /* Automap this on resolve. */ @@ -173,7 +173,7 @@ test_entryconn_rewrite_automap_ipv6(void *arg) ec3 = entry_connection_new(CONN_TYPE_AP, AF_INET6); get_options_mutable()->AutomapHostsOnResolve = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, tor_strdup(".")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, "."); parse_virtual_addr_network("FE80::/32", AF_INET6, 0, &msg); /* Automap this on resolve. */ @@ -489,8 +489,8 @@ test_entryconn_rewrite_automap_exit(void *arg) get_options_mutable()->AutomapHostsOnResolve = 1; get_options_mutable()->AllowDotExit = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, - tor_strdup(".EXIT")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, + ".EXIT"); parse_virtual_addr_network("127.1.0.0/16", AF_INET, 0, &msg); /* Automap this on resolve. */ @@ -574,8 +574,8 @@ test_entryconn_rewrite_mapaddress_automap_onion(void *arg) get_options_mutable()->AutomapHostsOnResolve = 1; get_options_mutable()->AllowDotExit = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, - tor_strdup(".onion")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, + ".onion"); parse_virtual_addr_network("192.168.0.0/16", AF_INET, 0, &msg); config_line_append(&get_options_mutable()->AddressMap, "MapAddress", "foo.onion abcdefghijklmnop.onion"); @@ -709,8 +709,8 @@ test_entryconn_rewrite_mapaddress_automap_onion2(void *arg) { char *msg = NULL; get_options_mutable()->AutomapHostsOnResolve = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, - tor_strdup(".onion")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, + ".onion"); parse_virtual_addr_network("192.168.0.0/16", AF_INET, 0, &msg); config_line_append(&get_options_mutable()->AddressMap, "MapAddress", "irc.example.com abcdefghijklmnop.onion"); @@ -736,8 +736,8 @@ test_entryconn_rewrite_mapaddress_automap_onion4(void *arg) { char *msg = NULL; get_options_mutable()->AutomapHostsOnResolve = 1; - smartlist_add(get_options_mutable()->AutomapHostsSuffixes, - tor_strdup(".onion")); + smartlist_add_strdup(get_options_mutable()->AutomapHostsSuffixes, + ".onion"); parse_virtual_addr_network("192.168.0.0/16", AF_INET, 0, &msg); test_entryconn_rewrite_mapaddress_automap_onion_common(arg, 0, 1); diff --git a/src/test/test_entrynodes.c b/src/test/test_entrynodes.c index b1c3accfab..95c8f6c336 100644 --- a/src/test/test_entrynodes.c +++ b/src/test/test_entrynodes.c @@ -3,6 +3,7 @@ #include "orconfig.h" +#define CIRCUITLIST_PRIVATE #define STATEFILE_PRIVATE #define ENTRYNODES_PRIVATE #define ROUTERLIST_PRIVATE @@ -10,9 +11,13 @@ #include "or.h" #include "test.h" +#include "bridges.h" +#include "circuitlist.h" #include "config.h" +#include "confparse.h" #include "entrynodes.h" #include "nodelist.h" +#include "networkstatus.h" #include "policies.h" #include "routerlist.h" #include "routerparse.h" @@ -21,6 +26,7 @@ #include "util.h" #include "test_helpers.h" +#include "log_test_helpers.h" /* TODO: * choose_random_entry() test with state set. @@ -39,37 +45,118 @@ get_or_state_replacement(void) return dummy_state; } +static networkstatus_t *dummy_consensus = NULL; + +static smartlist_t *big_fake_net_nodes = NULL; + +static smartlist_t * +bfn_mock_nodelist_get_list(void) +{ + return big_fake_net_nodes; +} + +static networkstatus_t * +bfn_mock_networkstatus_get_live_consensus(time_t now) +{ + (void)now; + return dummy_consensus; +} + +static const node_t * +bfn_mock_node_get_by_id(const char *id) +{ + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, + if (fast_memeq(n->identity, id, 20)) + return n); + + return NULL; +} + /* Unittest cleanup function: Cleanup the fake network. */ static int -fake_network_cleanup(const struct testcase_t *testcase, void *ptr) +big_fake_network_cleanup(const struct testcase_t *testcase, void *ptr) { (void) testcase; (void) ptr; - routerlist_free_all(); - nodelist_free_all(); - entry_guards_free_all(); + if (big_fake_net_nodes) { + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, { + tor_free(n->rs); + tor_free(n->md); + tor_free(n); + }); + smartlist_free(big_fake_net_nodes); + } + + UNMOCK(nodelist_get_list); + UNMOCK(node_get_by_id); + UNMOCK(get_or_state); + UNMOCK(networkstatus_get_live_consensus); or_state_free(dummy_state); + dummy_state = NULL; + tor_free(dummy_consensus); return 1; /* NOP */ } /* Unittest setup function: Setup a fake network. */ static void * -fake_network_setup(const struct testcase_t *testcase) +big_fake_network_setup(const struct testcase_t *testcase) { - (void) testcase; + int i; + + /* These are minimal node_t objects that only contain the aspects of node_t + * that we need for entrynodes.c. */ + const int N_NODES = 271; + + big_fake_net_nodes = smartlist_new(); + for (i = 0; i < N_NODES; ++i) { + node_t *n = tor_malloc_zero(sizeof(node_t)); + n->md = tor_malloc_zero(sizeof(microdesc_t)); + + crypto_rand(n->identity, sizeof(n->identity)); + n->rs = tor_malloc_zero(sizeof(routerstatus_t)); + + memcpy(n->rs->identity_digest, n->identity, DIGEST_LEN); + + n->is_running = n->is_valid = n->is_fast = n->is_stable = 1; + + n->rs->addr = 0x04020202; + n->rs->or_port = 1234; + n->rs->is_v2_dir = 1; + n->rs->has_bandwidth = 1; + n->rs->bandwidth_kb = 30; + + /* Call half of the nodes a possible guard. */ + if (i % 2 == 0) { + n->is_possible_guard = 1; + n->rs->guardfraction_percentage = 100; + n->rs->has_guardfraction = 1; + } + + smartlist_add(big_fake_net_nodes, n); + } - /* Setup fake state */ dummy_state = tor_malloc_zero(sizeof(or_state_t)); + dummy_consensus = tor_malloc_zero(sizeof(networkstatus_t)); + dummy_consensus->valid_after = approx_time() - 3600; + dummy_consensus->valid_until = approx_time() + 3600; + + MOCK(nodelist_get_list, bfn_mock_nodelist_get_list); + MOCK(node_get_by_id, bfn_mock_node_get_by_id); MOCK(get_or_state, get_or_state_replacement); - - /* Setup fake routerlist. */ - helper_setup_fake_routerlist(); - + MOCK(networkstatus_get_live_consensus, + bfn_mock_networkstatus_get_live_consensus); /* Return anything but NULL (it's interpreted as test fail) */ - return dummy_state; + return (void*)testcase; +} + +static time_t +mock_randomize_time_no_randomization(time_t a, time_t b) +{ + (void) b; + return a; } static or_options_t mocked_options; @@ -80,796 +167,2550 @@ mock_get_options(void) return &mocked_options; } -/** Test choose_random_entry() with none of our routers being guard nodes. */ +#define TEST_IPV4_ADDR "123.45.67.89" +#define TEST_IPV6_ADDR "[1234:5678:90ab:cdef::]" + static void -test_choose_random_entry_no_guards(void *arg) +test_node_preferred_orport(void *arg) { - const node_t *chosen_entry = NULL; - - (void) arg; - - MOCK(get_options, mock_get_options); + (void)arg; + tor_addr_t ipv4_addr; + const uint16_t ipv4_port = 4444; + tor_addr_t ipv6_addr; + const uint16_t ipv6_port = 6666; + routerinfo_t node_ri; + node_t node; + tor_addr_port_t ap; - /* Check that we get a guard if it passes preferred - * address settings */ + /* Setup options */ memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientPreferIPv6ORPort = 0; - - /* Try to pick an entry even though none of our routers are guards. */ - chosen_entry = choose_random_entry(NULL); - - /* Unintuitively, we actually pick a random node as our entry, - because router_choose_random_node() relaxes its constraints if it - can't find a proper entry guard. */ - tt_assert(chosen_entry); + /* We don't test ClientPreferIPv6ORPort here, because it's used in + * nodelist_set_consensus to setup node.ipv6_preferred, which we set + * directly. */ + MOCK(get_options, mock_get_options); - /* And with the other IP version active */ - mocked_options.ClientUseIPv6 = 1; - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + /* Setup IP addresses */ + tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR); + tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR); - /* And with the preference on auto */ - mocked_options.ClientPreferIPv6ORPort = -1; - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + /* Setup node_ri */ + memset(&node_ri, 0, sizeof(node_ri)); + node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr); + node_ri.or_port = ipv4_port; + tor_addr_copy(&node_ri.ipv6_addr, &ipv6_addr); + node_ri.ipv6_orport = ipv6_port; - /* Check that we don't get a guard if it doesn't pass mandatory address - * settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 0; - mocked_options.ClientPreferIPv6ORPort = 0; + /* Setup node */ + memset(&node, 0, sizeof(node)); + node.ri = &node_ri; - chosen_entry = choose_random_entry(NULL); + /* Check the preferred address is IPv4 if we're only using IPv4, regardless + * of whether we prefer it or not */ + mocked_options.ClientUseIPv4 = 1; + mocked_options.ClientUseIPv6 = 0; + node.ipv6_preferred = 0; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); + tt_assert(ap.port == ipv4_port); - /* If we don't allow IPv4 at all, we don't get a guard*/ - tt_assert(!chosen_entry); + node.ipv6_preferred = 1; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); + tt_assert(ap.port == ipv4_port); - /* Check that we get a guard if it passes allowed but not preferred address - * settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); + /* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but + * don't prefer the IPv6 address */ mocked_options.ClientUseIPv4 = 1; mocked_options.ClientUseIPv6 = 1; - mocked_options.ClientPreferIPv6ORPort = 1; - - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + node.ipv6_preferred = 0; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); + tt_assert(ap.port == ipv4_port); - /* Check that we get a guard if it passes preferred address settings when - * they're auto */ - memset(&mocked_options, 0, sizeof(mocked_options)); + /* Check the preferred address is IPv6 if we prefer it and + * ClientUseIPv6 is 1, regardless of ClientUseIPv4 */ mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientPreferIPv6ORPort = -1; + mocked_options.ClientUseIPv6 = 1; + node.ipv6_preferred = 1; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); + tt_assert(ap.port == ipv6_port); - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + mocked_options.ClientUseIPv4 = 0; + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); + tt_assert(ap.port == ipv6_port); - /* And with IPv6 active */ + /* Check the preferred address is IPv6 if we don't prefer it, but + * ClientUseIPv4 is 0 */ + mocked_options.ClientUseIPv4 = 0; mocked_options.ClientUseIPv6 = 1; - - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); + node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options); + node_get_pref_orport(&node, &ap); + tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); + tt_assert(ap.port == ipv6_port); done: - memset(&mocked_options, 0, sizeof(mocked_options)); UNMOCK(get_options); } -/** Test choose_random_entry() with only one of our routers being a - guard node. */ static void -test_choose_random_entry_one_possible_guard(void *arg) +test_entry_guard_describe(void *arg) { - const node_t *chosen_entry = NULL; - node_t *the_guard = NULL; - smartlist_t *our_nodelist = NULL; + (void)arg; + entry_guard_t g; + memset(&g, 0, sizeof(g)); + strlcpy(g.nickname, "okefenokee", sizeof(g.nickname)); + memcpy(g.identity, "theforestprimeval---", DIGEST_LEN); - (void) arg; + tt_str_op(entry_guard_describe(&g), OP_EQ, + "okefenokee ($746865666F726573747072696D6576616C2D2D2D)"); - MOCK(get_options, mock_get_options); - - /* Set one of the nodes to be a guard. */ - our_nodelist = nodelist_get_list(); - the_guard = smartlist_get(our_nodelist, 4); /* chosen by fair dice roll */ - the_guard->is_possible_guard = 1; + done: + ; +} - /* Check that we get the guard if it passes preferred - * address settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientPreferIPv6ORPort = 0; +static void +test_entry_guard_randomize_time(void *arg) +{ + const time_t now = 1479153573; + const int delay = 86400; + const int N = 1000; + (void)arg; - /* Pick an entry. Make sure we pick the node we marked as guard. */ - chosen_entry = choose_random_entry(NULL); - tt_ptr_op(chosen_entry, OP_EQ, the_guard); + time_t t; + int i; + for (i = 0; i < N; ++i) { + t = randomize_time(now, delay); + tt_int_op(t, OP_LE, now); + tt_int_op(t, OP_GE, now-delay); + } - /* And with the other IP version active */ - mocked_options.ClientUseIPv6 = 1; - chosen_entry = choose_random_entry(NULL); - tt_ptr_op(chosen_entry, OP_EQ, the_guard); + /* now try the corner cases */ + for (i = 0; i < N; ++i) { + t = randomize_time(100, delay); + tt_int_op(t, OP_GE, 1); + tt_int_op(t, OP_LE, 100); - /* And with the preference on auto */ - mocked_options.ClientPreferIPv6ORPort = -1; - chosen_entry = choose_random_entry(NULL); - tt_ptr_op(chosen_entry, OP_EQ, the_guard); + t = randomize_time(0, delay); + tt_int_op(t, OP_EQ, 1); + } - /* Check that we don't get a guard if it doesn't pass mandatory address - * settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 0; - mocked_options.ClientPreferIPv6ORPort = 0; + done: + ; +} - chosen_entry = choose_random_entry(NULL); +static void +test_entry_guard_encode_for_state_minimal(void *arg) +{ + (void) arg; + entry_guard_t *eg = tor_malloc_zero(sizeof(entry_guard_t)); - /* If we don't allow IPv4 at all, we don't get a guard*/ - tt_assert(!chosen_entry); + eg->selection_name = tor_strdup("wubwub"); + memcpy(eg->identity, "plurpyflurpyslurpydo", DIGEST_LEN); + eg->sampled_on_date = 1479081600; + eg->confirmed_idx = -1; - /* Check that we get a node if it passes allowed but not preferred - * address settings */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 1; - mocked_options.ClientPreferIPv6ORPort = 1; + char *s = NULL; + s = entry_guard_encode_for_state(eg); - chosen_entry = choose_random_entry(NULL); + tt_str_op(s, OP_EQ, + "in=wubwub " + "rsa_id=706C75727079666C75727079736C75727079646F " + "sampled_on=2016-11-14T00:00:00 " + "listed=0"); - /* We disable the guard check and the preferred address check at the same - * time, so we can't be sure we get the guard */ - tt_assert(chosen_entry); + done: + entry_guard_free(eg); + tor_free(s); +} - /* Check that we get a node if it is allowed but not preferred when settings - * are auto */ - memset(&mocked_options, 0, sizeof(mocked_options)); - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientPreferIPv6ORPort = -1; +static void +test_entry_guard_encode_for_state_maximal(void *arg) +{ + (void) arg; + entry_guard_t *eg = tor_malloc_zero(sizeof(entry_guard_t)); + + strlcpy(eg->nickname, "Fred", sizeof(eg->nickname)); + eg->selection_name = tor_strdup("default"); + memcpy(eg->identity, "plurpyflurpyslurpydo", DIGEST_LEN); + eg->bridge_addr = tor_malloc_zero(sizeof(tor_addr_port_t)); + tor_addr_from_ipv4h(&eg->bridge_addr->addr, 0x08080404); + eg->bridge_addr->port = 9999; + eg->sampled_on_date = 1479081600; + eg->sampled_by_version = tor_strdup("1.2.3"); + eg->unlisted_since_date = 1479081645; + eg->currently_listed = 1; + eg->confirmed_on_date = 1479081690; + eg->confirmed_idx = 333; + eg->extra_state_fields = tor_strdup("and the green grass grew all around"); + + char *s = NULL; + s = entry_guard_encode_for_state(eg); + + tt_str_op(s, OP_EQ, + "in=default " + "rsa_id=706C75727079666C75727079736C75727079646F " + "bridge_addr=8.8.4.4:9999 " + "nickname=Fred " + "sampled_on=2016-11-14T00:00:00 " + "sampled_by=1.2.3 " + "unlisted_since=2016-11-14T00:00:45 " + "listed=1 " + "confirmed_on=2016-11-14T00:01:30 " + "confirmed_idx=333 " + "and the green grass grew all around"); - chosen_entry = choose_random_entry(NULL); + done: + entry_guard_free(eg); + tor_free(s); +} - /* We disable the guard check and the preferred address check at the same - * time, so we can't be sure we get the guard */ - tt_assert(chosen_entry); +static void +test_entry_guard_parse_from_state_minimal(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; + entry_guard_t *eg = NULL; + time_t t = approx_time(); + + eg = entry_guard_parse_from_state( + "in=default_plus " + "rsa_id=596f75206d6179206e656564206120686f626279"); + tt_assert(eg); + + tt_str_op(eg->selection_name, OP_EQ, "default_plus"); + test_mem_op_hex(eg->identity, OP_EQ, + "596f75206d6179206e656564206120686f626279"); + tt_str_op(eg->nickname, OP_EQ, "$596F75206D6179206E656564206120686F626279"); + tt_ptr_op(eg->bridge_addr, OP_EQ, NULL); + tt_i64_op(eg->sampled_on_date, OP_GE, t); + tt_i64_op(eg->sampled_on_date, OP_LE, t+86400); + tt_i64_op(eg->unlisted_since_date, OP_EQ, 0); + tt_ptr_op(eg->sampled_by_version, OP_EQ, NULL); + tt_int_op(eg->currently_listed, OP_EQ, 0); + tt_i64_op(eg->confirmed_on_date, OP_EQ, 0); + tt_int_op(eg->confirmed_idx, OP_EQ, -1); + + tt_int_op(eg->last_tried_to_connect, OP_EQ, 0); + tt_int_op(eg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); - /* and with IPv6 active */ - mocked_options.ClientUseIPv6 = 1; + done: + entry_guard_free(eg); + tor_free(mem_op_hex_tmp); +} - chosen_entry = choose_random_entry(NULL); - tt_assert(chosen_entry); +static void +test_entry_guard_parse_from_state_maximal(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; + entry_guard_t *eg = NULL; + + eg = entry_guard_parse_from_state( + "in=fred " + "rsa_id=706C75727079666C75727079736C75727079646F " + "bridge_addr=[1::3]:9999 " + "nickname=Fred " + "sampled_on=2016-11-14T00:00:00 " + "sampled_by=1.2.3 " + "unlisted_since=2016-11-14T00:00:45 " + "listed=1 " + "confirmed_on=2016-11-14T00:01:30 " + "confirmed_idx=333 " + "and the green grass grew all around " + "rsa_id=all,around"); + tt_assert(eg); + + test_mem_op_hex(eg->identity, OP_EQ, + "706C75727079666C75727079736C75727079646F"); + tt_str_op(fmt_addr(&eg->bridge_addr->addr), OP_EQ, "1::3"); + tt_int_op(eg->bridge_addr->port, OP_EQ, 9999); + tt_str_op(eg->nickname, OP_EQ, "Fred"); + tt_i64_op(eg->sampled_on_date, OP_EQ, 1479081600); + tt_i64_op(eg->unlisted_since_date, OP_EQ, 1479081645); + tt_str_op(eg->sampled_by_version, OP_EQ, "1.2.3"); + tt_int_op(eg->currently_listed, OP_EQ, 1); + tt_i64_op(eg->confirmed_on_date, OP_EQ, 1479081690); + tt_int_op(eg->confirmed_idx, OP_EQ, 333); + tt_str_op(eg->extra_state_fields, OP_EQ, + "and the green grass grew all around rsa_id=all,around"); + + tt_int_op(eg->last_tried_to_connect, OP_EQ, 0); + tt_int_op(eg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); done: - memset(&mocked_options, 0, sizeof(mocked_options)); - UNMOCK(get_options); + entry_guard_free(eg); + tor_free(mem_op_hex_tmp); } -/** Helper to conduct tests for populate_live_entry_guards(). +static void +test_entry_guard_parse_from_state_failure(void *arg) +{ + (void)arg; + entry_guard_t *eg = NULL; + + /* no selection */ + eg = entry_guard_parse_from_state( + "rsa_id=596f75206d6179206e656564206120686f626270"); + tt_assert(! eg); + + /* no RSA ID. */ + eg = entry_guard_parse_from_state("in=default nickname=Fred"); + tt_assert(! eg); + + /* Bad RSA ID: bad character. */ + eg = entry_guard_parse_from_state( + "in=default " + "rsa_id=596f75206d6179206e656564206120686f62627q"); + tt_assert(! eg); + + /* Bad RSA ID: too long.*/ + eg = entry_guard_parse_from_state( + "in=default " + "rsa_id=596f75206d6179206e656564206120686f6262703"); + tt_assert(! eg); + + /* Bad RSA ID: too short.*/ + eg = entry_guard_parse_from_state( + "in=default " + "rsa_id=596f75206d6179206e65656420612"); + tt_assert(! eg); - This test adds some entry guards to our list, and then tests - populate_live_entry_guards() to mke sure it filters them correctly. + done: + entry_guard_free(eg); +} - <b>num_needed</b> is the number of guard nodes we support. It's - configurable to make sure we function properly with 1 or 3 guard - nodes configured. -*/ static void -populate_live_entry_guards_test_helper(int num_needed) +test_entry_guard_parse_from_state_partial_failure(void *arg) +{ + (void)arg; + char *mem_op_hex_tmp = NULL; + entry_guard_t *eg = NULL; + time_t t = approx_time(); + + eg = entry_guard_parse_from_state( + "in=default " + "rsa_id=706C75727079666C75727079736C75727079646F " + "bridge_addr=1.2.3.3.4:5 " + "nickname=FredIsANodeWithAStrangeNicknameThatIsTooLong " + "sampled_on=2016-11-14T00:00:99 " + "sampled_by=1.2.3 stuff in the middle " + "unlisted_since=2016-xx-14T00:00:45 " + "listed=0 " + "confirmed_on=2016-11-14T00:01:30zz " + "confirmed_idx=idx " + "and the green grass grew all around " + "rsa_id=all,around"); + tt_assert(eg); + + test_mem_op_hex(eg->identity, OP_EQ, + "706C75727079666C75727079736C75727079646F"); + tt_str_op(eg->nickname, OP_EQ, "FredIsANodeWithAStrangeNicknameThatIsTooL"); + tt_ptr_op(eg->bridge_addr, OP_EQ, NULL); + tt_i64_op(eg->sampled_on_date, OP_EQ, t); + tt_i64_op(eg->unlisted_since_date, OP_EQ, 0); + tt_str_op(eg->sampled_by_version, OP_EQ, "1.2.3"); + tt_int_op(eg->currently_listed, OP_EQ, 0); + tt_i64_op(eg->confirmed_on_date, OP_EQ, 0); + tt_int_op(eg->confirmed_idx, OP_EQ, -1); + tt_str_op(eg->extra_state_fields, OP_EQ, + "stuff in the middle and the green grass grew all around " + "rsa_id=all,around"); + + tt_int_op(eg->last_tried_to_connect, OP_EQ, 0); + tt_int_op(eg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + done: + entry_guard_free(eg); + tor_free(mem_op_hex_tmp); +} + +static int +mock_entry_guard_is_listed(guard_selection_t *gs, const entry_guard_t *guard) { - smartlist_t *our_nodelist = NULL; - smartlist_t *live_entry_guards = smartlist_new(); - const smartlist_t *all_entry_guards = get_entry_guards(); - or_options_t *options = get_options_mutable(); - int retval; + (void)gs; + (void)guard; + return 1; +} - /* Set NumEntryGuards to the provided number. */ - options->NumEntryGuards = num_needed; - tt_int_op(num_needed, OP_EQ, decide_num_guards(options, 0)); +static void +test_entry_guard_parse_from_state_full(void *arg) +{ + (void)arg; + /* Here's a state I made while testing. The identities and locations for + * the bridges are redacted. */ + const char STATE[] = + "Guard in=default rsa_id=214F44BD5B638E8C817D47FF7C97397790BF0345 " + "nickname=TotallyNinja sampled_on=2016-11-12T19:32:49 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1\n" + "Guard in=default rsa_id=052900AB0EA3ED54BAB84AE8A99E74E8693CE2B2 " + "nickname=5OfNovember sampled_on=2016-11-20T04:32:05 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-22T08:13:28 confirmed_idx=0 " + "pb_circ_attempts=4.000000 pb_circ_successes=2.000000 " + "pb_successful_circuits_closed=2.000000\n" + "Guard in=default rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-24T08:45:30 confirmed_idx=4 " + "pb_circ_attempts=5.000000 pb_circ_successes=5.000000 " + "pb_successful_circuits_closed=5.000000\n" + "Guard in=wobblesome rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1\n" + "Guard in=default rsa_id=E9025AD60D86875D5F11548D536CC6AF60F0EF5E " + "nickname=maibrunn sampled_on=2016-11-25T22:36:38 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n" + "Guard in=default rsa_id=DCD30B90BA3A792DA75DC54A327EF353FB84C38E " + "nickname=Unnamed sampled_on=2016-11-25T14:34:00 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n" + "Guard in=bridges rsa_id=8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E " + "bridge_addr=24.1.1.1:443 sampled_on=2016-11-25T06:44:14 " + "sampled_by=0.3.0.0-alpha-dev listed=1 " + "confirmed_on=2016-11-29T10:36:06 confirmed_idx=0 " + "pb_circ_attempts=8.000000 pb_circ_successes=8.000000 " + "pb_successful_circuits_closed=13.000000\n" + "Guard in=bridges rsa_id=5800000000000000000000000000000000000000 " + "bridge_addr=37.218.246.143:28366 " + "sampled_on=2016-11-18T15:07:34 sampled_by=0.3.0.0-alpha-dev listed=1\n"; + + config_line_t *lines = NULL; + or_state_t *state = tor_malloc_zero(sizeof(or_state_t)); + int r = config_get_lines(STATE, &lines, 0); + char *msg = NULL; + smartlist_t *text = smartlist_new(); + char *joined = NULL; - /* The global entry guards smartlist should be empty now. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 0); + // So nodes aren't expired. This is Tue, 13 Dec 2016 09:37:14 GMT + update_approx_time(1481621834); - /* Walk the nodelist and add all nodes as entry guards. */ - our_nodelist = nodelist_get_list(); - tt_int_op(smartlist_len(our_nodelist), OP_EQ, HELPER_NUMBER_OF_DESCRIPTORS); + MOCK(entry_guard_is_listed, mock_entry_guard_is_listed); - SMARTLIST_FOREACH_BEGIN(our_nodelist, const node_t *, node) { - const node_t *node_tmp; - node_tmp = add_an_entry_guard(node, 0, 1, 0, 0); - tt_assert(node_tmp); - } SMARTLIST_FOREACH_END(node); + dummy_state = state; + MOCK(get_or_state, + get_or_state_replacement); - /* Make sure the nodes were added as entry guards. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, - HELPER_NUMBER_OF_DESCRIPTORS); + tt_assert(r == 0); + tt_assert(lines); + + state->Guard = lines; + + /* Try it first without setting the result. */ + r = entry_guards_parse_state(state, 0, &msg); + tt_assert(r == 0); + guard_selection_t *gs_br = + get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0); + tt_assert(!gs_br); + + r = entry_guards_parse_state(state, 1, &msg); + tt_assert(r == 0); + gs_br = get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0); + guard_selection_t *gs_df = + get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + guard_selection_t *gs_wb = + get_guard_selection_by_name("wobblesome", GS_TYPE_NORMAL, 0); + + tt_assert(gs_br); + tt_assert(gs_df); + tt_assert(gs_wb); + + tt_int_op(smartlist_len(gs_df->sampled_entry_guards), OP_EQ, 5); + tt_int_op(smartlist_len(gs_br->sampled_entry_guards), OP_EQ, 2); + tt_int_op(smartlist_len(gs_wb->sampled_entry_guards), OP_EQ, 1); + + /* Try again; make sure it doesn't double-add the guards. */ + r = entry_guards_parse_state(state, 1, &msg); + tt_assert(r == 0); + gs_br = get_guard_selection_by_name("bridges", GS_TYPE_BRIDGE, 0); + gs_df = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + tt_assert(gs_br); + tt_assert(gs_df); + tt_int_op(smartlist_len(gs_df->sampled_entry_guards), OP_EQ, 5); + tt_int_op(smartlist_len(gs_br->sampled_entry_guards), OP_EQ, 2); + + /* Re-encode; it should be the same... almost. */ + { + /* (Make a guard nonpersistent first) */ + entry_guard_t *g = smartlist_get(gs_df->sampled_entry_guards, 0); + g->is_persistent = 0; + } + config_free_lines(lines); + lines = state->Guard = NULL; // to prevent double-free. + entry_guards_update_state(state); + tt_assert(state->Guard); + lines = state->Guard; + + config_line_t *ln; + for (ln = lines; ln; ln = ln->next) { + smartlist_add_asprintf(text, "%s %s\n",ln->key, ln->value); + } + joined = smartlist_join_strings(text, "", 0, NULL); + tt_str_op(joined, OP_EQ, + "Guard in=default rsa_id=052900AB0EA3ED54BAB84AE8A99E74E8693CE2B2 " + "nickname=5OfNovember sampled_on=2016-11-20T04:32:05 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-22T08:13:28 confirmed_idx=0 " + "pb_circ_attempts=4.000000 pb_circ_successes=2.000000 " + "pb_successful_circuits_closed=2.000000\n" + "Guard in=default rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-24T08:45:30 confirmed_idx=1 " + "pb_circ_attempts=5.000000 pb_circ_successes=5.000000 " + "pb_successful_circuits_closed=5.000000\n" + "Guard in=default rsa_id=E9025AD60D86875D5F11548D536CC6AF60F0EF5E " + "nickname=maibrunn sampled_on=2016-11-25T22:36:38 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n" + "Guard in=default rsa_id=DCD30B90BA3A792DA75DC54A327EF353FB84C38E " + "nickname=Unnamed sampled_on=2016-11-25T14:34:00 " + "sampled_by=0.3.0.0-alpha-dev listed=1\n" + "Guard in=wobblesome rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1\n" + "Guard in=bridges rsa_id=8FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF2E " + "bridge_addr=24.1.1.1:443 sampled_on=2016-11-25T06:44:14 " + "sampled_by=0.3.0.0-alpha-dev listed=1 " + "confirmed_on=2016-11-29T10:36:06 confirmed_idx=0 " + "pb_circ_attempts=8.000000 pb_circ_successes=8.000000 " + "pb_successful_circuits_closed=13.000000\n" + "Guard in=bridges rsa_id=5800000000000000000000000000000000000000 " + "bridge_addr=37.218.246.143:28366 " + "sampled_on=2016-11-18T15:07:34 sampled_by=0.3.0.0-alpha-dev listed=1\n"); - /* Ensure that all the possible entry guards are enough to satisfy us. */ - tt_int_op(smartlist_len(all_entry_guards), OP_GE, num_needed); + done: + config_free_lines(lines); + tor_free(state); + tor_free(msg); + UNMOCK(get_or_state); + UNMOCK(entry_guard_is_listed); + SMARTLIST_FOREACH(text, char *, cp, tor_free(cp)); + smartlist_free(text); + tor_free(joined); +} - /* Walk the entry guard list for some sanity checking */ - SMARTLIST_FOREACH_BEGIN(all_entry_guards, const entry_guard_t *, entry) { - /* Since we called add_an_entry_guard() with 'for_discovery' being - False, all guards should have made_contact enabled. */ - tt_int_op(entry->made_contact, OP_EQ, 1); +static void +test_entry_guard_parse_from_state_broken(void *arg) +{ + (void)arg; + /* Here's a variation on the previous state. Every line but the first is + * busted somehow. */ + const char STATE[] = + /* Okay. */ + "Guard in=default rsa_id=214F44BD5B638E8C817D47FF7C97397790BF0345 " + "nickname=TotallyNinja sampled_on=2016-11-12T19:32:49 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1\n" + /* No selection listed. */ + "Guard rsa_id=052900AB0EA3ED54BAB84AE8A99E74E8693CE2B2 " + "nickname=5OfNovember sampled_on=2016-11-20T04:32:05 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-22T08:13:28 confirmed_idx=0 " + "pb_circ_attempts=4.000000 pb_circ_successes=2.000000 " + "pb_successful_circuits_closed=2.000000\n" + /* Selection is "legacy"!! */ + "Guard in=legacy rsa_id=7B700C0C207EBD0002E00F499BE265519AC3C25A " + "nickname=dc6jgk11 sampled_on=2016-11-28T11:50:13 " + "sampled_by=0.3.0.0-alpha-dev " + "listed=1 confirmed_on=2016-11-24T08:45:30 confirmed_idx=4 " + "pb_circ_attempts=5.000000 pb_circ_successes=5.000000 " + "pb_successful_circuits_closed=5.000000\n"; + + config_line_t *lines = NULL; + or_state_t *state = tor_malloc_zero(sizeof(or_state_t)); + int r = config_get_lines(STATE, &lines, 0); + char *msg = NULL; - } SMARTLIST_FOREACH_END(entry); + dummy_state = state; + MOCK(get_or_state, + get_or_state_replacement); - /* First, try to get some fast guards. This should fail. */ - retval = populate_live_entry_guards(live_entry_guards, - all_entry_guards, - NULL, - NO_DIRINFO, /* Don't care about DIRINFO*/ - 0, 0, - 1); /* We want fast guard! */ - tt_int_op(retval, OP_EQ, 0); - tt_int_op(smartlist_len(live_entry_guards), OP_EQ, 0); + tt_assert(r == 0); + tt_assert(lines); - /* Now try to get some stable guards. This should fail too. */ - retval = populate_live_entry_guards(live_entry_guards, - all_entry_guards, - NULL, - NO_DIRINFO, - 0, - 1, /* We want stable guard! */ - 0); - tt_int_op(retval, OP_EQ, 0); - tt_int_op(smartlist_len(live_entry_guards), OP_EQ, 0); + state->Guard = lines; - /* Now try to get any guard we can find. This should succeed. */ - retval = populate_live_entry_guards(live_entry_guards, - all_entry_guards, - NULL, - NO_DIRINFO, - 0, 0, 0); /* No restrictions! */ + /* First, no-set case. we should get an error. */ + r = entry_guards_parse_state(state, 0, &msg); + tt_int_op(r, OP_LT, 0); + tt_ptr_op(msg, OP_NE, NULL); + /* And we shouldn't have made anything. */ + guard_selection_t *gs_df = + get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + tt_assert(gs_df == NULL); + tor_free(msg); - /* Since we had more than enough guards in 'all_entry_guards', we - should have added 'num_needed' of them to live_entry_guards. - 'retval' should be 1 since we now have enough live entry guards - to pick one. */ - tt_int_op(retval, OP_EQ, 1); - tt_int_op(smartlist_len(live_entry_guards), OP_EQ, num_needed); + /* Now see about the set case (which shouldn't happen IRL) */ + r = entry_guards_parse_state(state, 1, &msg); + tt_int_op(r, OP_LT, 0); + tt_ptr_op(msg, OP_NE, NULL); + gs_df = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + tt_assert(gs_df != NULL); + tt_int_op(smartlist_len(gs_df->sampled_entry_guards), OP_EQ, 1); done: - smartlist_free(live_entry_guards); + config_free_lines(lines); + tor_free(state); + tor_free(msg); + UNMOCK(get_or_state); } -/* Test populate_live_entry_guards() for 1 guard node. */ static void -test_populate_live_entry_guards_1guard(void *arg) +test_entry_guard_get_guard_selection_by_name(void *arg) { - (void) arg; + (void)arg; + guard_selection_t *gs1, *gs2, *gs3; + + gs1 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 0); + tt_assert(gs1 == NULL); + gs1 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 1); + tt_assert(gs1 != NULL); + gs2 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 1); + tt_assert(gs2 == gs1); + gs2 = get_guard_selection_by_name("unlikely", GS_TYPE_NORMAL, 0); + tt_assert(gs2 == gs1); + + gs2 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 0); + tt_assert(gs2 == NULL); + gs2 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 1); + tt_assert(gs2 != NULL); + tt_assert(gs2 != gs1); + gs3 = get_guard_selection_by_name("implausible", GS_TYPE_NORMAL, 0); + tt_assert(gs3 == gs2); + + gs3 = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 0); + tt_assert(gs3 == NULL); + gs3 = get_guard_selection_by_name("default", GS_TYPE_NORMAL, 1); + tt_assert(gs3 != NULL); + tt_assert(gs3 != gs2); + tt_assert(gs3 != gs1); + tt_assert(gs3 == get_guard_selection_info()); - populate_live_entry_guards_test_helper(1); + done: + entry_guards_free_all(); } -/* Test populate_live_entry_guards() for 3 guard nodes. */ static void -test_populate_live_entry_guards_3guards(void *arg) +test_entry_guard_choose_selection_initial(void *arg) { - (void) arg; + /* Tests for picking our initial guard selection (based on having had + * no previous selection */ + (void)arg; + guard_selection_type_t type = GS_TYPE_INFER; + const char *name = choose_guard_selection(get_options(), + dummy_consensus, NULL, &type); + tt_str_op(name, OP_EQ, "default"); + tt_int_op(type, OP_EQ, GS_TYPE_NORMAL); + + /* If we're using bridges, we get the bridge selection. */ + get_options_mutable()->UseBridges = 1; + name = choose_guard_selection(get_options(), + dummy_consensus, NULL, &type); + tt_str_op(name, OP_EQ, "bridges"); + tt_int_op(type, OP_EQ, GS_TYPE_BRIDGE); + get_options_mutable()->UseBridges = 0; + + /* If we discard >99% of our guards, though, we should be in the restricted + * set. */ + tt_assert(get_options_mutable()->EntryNodes == NULL); + get_options_mutable()->EntryNodes = routerset_new(); + routerset_parse(get_options_mutable()->EntryNodes, "1.0.0.0/8", "foo"); + name = choose_guard_selection(get_options(), + dummy_consensus, NULL, &type); + tt_str_op(name, OP_EQ, "restricted"); + tt_int_op(type, OP_EQ, GS_TYPE_RESTRICTED); - populate_live_entry_guards_test_helper(3); + done: + ; } -/** Append some EntryGuard lines to the Tor state at <b>state</b>. - - <b>entry_guard_lines</b> is a smartlist containing 2-tuple - smartlists that carry the key and values of the statefile. - As an example: - entry_guard_lines = - (("EntryGuard", "name 67E72FF33D7D41BF11C569646A0A7B4B188340DF DirCache"), - ("EntryGuardDownSince", "2014-06-07 16:02:46 2014-06-07 16:02:46")) -*/ static void -state_insert_entry_guard_helper(or_state_t *state, - smartlist_t *entry_guard_lines) +test_entry_guard_add_single_guard(void *arg) { - config_line_t **next, *line; - - next = &state->EntryGuards; - *next = NULL; - - /* Loop over all the state lines in the smartlist */ - SMARTLIST_FOREACH_BEGIN(entry_guard_lines, const smartlist_t *,state_lines) { - /* Get key and value for each line */ - const char *state_key = smartlist_get(state_lines, 0); - const char *state_value = smartlist_get(state_lines, 1); + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + /* 1: Add a single guard to the sample. */ + node_t *n1 = smartlist_get(big_fake_net_nodes, 0); + time_t now = approx_time(); + tt_assert(n1->is_possible_guard == 1); + entry_guard_t *g1 = entry_guard_add_to_sample(gs, n1); + tt_assert(g1); + + /* Make sure its fields look right. */ + tt_mem_op(n1->identity, OP_EQ, g1->identity, DIGEST_LEN); + tt_i64_op(g1->sampled_on_date, OP_GE, now - 12*86400); + tt_i64_op(g1->sampled_on_date, OP_LE, now); + tt_str_op(g1->sampled_by_version, OP_EQ, VERSION); + tt_assert(g1->currently_listed == 1); + tt_i64_op(g1->confirmed_on_date, OP_EQ, 0); + tt_int_op(g1->confirmed_idx, OP_EQ, -1); + tt_int_op(g1->last_tried_to_connect, OP_EQ, 0); + tt_uint_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + tt_i64_op(g1->failing_since, OP_EQ, 0); + tt_assert(g1->is_filtered_guard == 1); + tt_assert(g1->is_usable_filtered_guard == 1); + tt_assert(g1->is_primary == 0); + tt_assert(g1->extra_state_fields == NULL); + + /* Make sure it got added. */ + tt_int_op(1, OP_EQ, smartlist_len(gs->sampled_entry_guards)); + tt_ptr_op(g1, OP_EQ, smartlist_get(gs->sampled_entry_guards, 0)); + tt_ptr_op(g1, OP_EQ, get_sampled_guard_with_id(gs, (uint8_t*)n1->identity)); + const uint8_t bad_id[20] = {0}; + tt_ptr_op(NULL, OP_EQ, get_sampled_guard_with_id(gs, bad_id)); - *next = line = tor_malloc_zero(sizeof(config_line_t)); - line->key = tor_strdup(state_key); - tor_asprintf(&line->value, "%s", state_value); - next = &(line->next); - } SMARTLIST_FOREACH_END(state_lines); + done: + guard_selection_free(gs); } -/** Free memory occupied by <b>entry_guard_lines</b>. */ static void -state_lines_free(smartlist_t *entry_guard_lines) +test_entry_guard_node_filter(void *arg) { - SMARTLIST_FOREACH_BEGIN(entry_guard_lines, smartlist_t *, state_lines) { - char *state_key = smartlist_get(state_lines, 0); - char *state_value = smartlist_get(state_lines, 1); + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + bridge_line_t *bl = NULL; + + /* Initialize a bunch of node objects that are all guards. */ +#define NUM 7 + node_t *n[NUM]; + entry_guard_t *g[NUM]; + int i; + for (i=0; i < NUM; ++i) { + n[i] = smartlist_get(big_fake_net_nodes, i*2); // even ones are guards. + g[i] = entry_guard_add_to_sample(gs, n[i]); + + // everything starts out filtered-in + tt_assert(g[i]->is_filtered_guard == 1); + tt_assert(g[i]->is_usable_filtered_guard == 1); + } + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, NUM); - tor_free(state_key); - tor_free(state_value); - smartlist_free(state_lines); - } SMARTLIST_FOREACH_END(state_lines); + /* Make sure refiltering doesn't hurt */ + entry_guards_update_filtered_sets(gs); + for (i = 0; i < NUM; ++i) { + tt_assert(g[i]->is_filtered_guard == 1); + tt_assert(g[i]->is_usable_filtered_guard == 1); + } + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, NUM); - smartlist_free(entry_guard_lines); -} + /* Now start doing things to make the guards get filtered out, 1 by 1. */ -/* Tests entry_guards_parse_state(). It creates a fake Tor state with - a saved entry guard and makes sure that Tor can parse it and - creates the right entry node out of it. -*/ -static void -test_entry_guards_parse_state_simple(void *arg) -{ - or_state_t *state = or_state_new(); - const smartlist_t *all_entry_guards = get_entry_guards(); - smartlist_t *entry_state_lines = smartlist_new(); - char *msg = NULL; - int retval; + /* 0: Not listed. */ + g[0]->currently_listed = 0; - /* Details of our fake guard node */ - const char *nickname = "hagbard"; - const char *fpr = "B29D536DD1752D542E1FBB3C9CE4449D51298212"; - const char *tor_version = "0.2.5.3-alpha-dev"; - const char *added_at = get_yesterday_date_str(); - const char *unlisted_since = "2014-06-08 16:16:50"; + /* 1: path bias says this guard is maybe eeeevil. */ + g[1]->pb.path_bias_disabled = 1; - (void) arg; + /* 2: Unreachable address. */ + n[2]->rs->addr = 0; - /* The global entry guards smartlist should be empty now. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 0); + /* 3: ExcludeNodes */ + n[3]->rs->addr = 0x90902020; + routerset_free(get_options_mutable()->ExcludeNodes); + get_options_mutable()->ExcludeNodes = routerset_new(); + routerset_parse(get_options_mutable()->ExcludeNodes, "144.144.0.0/16", ""); - { /* Prepare the state entry */ + /* 4: Bridge. */ + sweep_bridge_list(); + bl = tor_malloc_zero(sizeof(bridge_line_t)); + tor_addr_from_ipv4h(&bl->addr, n[4]->rs->addr); + bl->port = n[4]->rs->or_port; + memcpy(bl->digest, n[4]->identity, 20); + bridge_add_from_config(bl); + bl = NULL; // prevent free. - /* Prepare the smartlist to hold the key/value of each line */ - smartlist_t *state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuard"); - smartlist_add_asprintf(state_line, "%s %s %s", nickname, fpr, "DirCache"); - smartlist_add(entry_state_lines, state_line); + /* 5: Unreachable. This stays in the filter, but isn't in usable-filtered */ + g[5]->last_tried_to_connect = approx_time(); // prevent retry. + g[5]->is_reachable = GUARD_REACHABLE_NO; - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardAddedBy"); - smartlist_add_asprintf(state_line, "%s %s %s", fpr, tor_version, added_at); - smartlist_add(entry_state_lines, state_line); + /* 6: no change. */ - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardUnlistedSince"); - smartlist_add_asprintf(state_line, "%s", unlisted_since); - smartlist_add(entry_state_lines, state_line); + /* Now refilter and inspect. */ + entry_guards_update_filtered_sets(gs); + for (i = 0; i < NUM; ++i) { + tt_assert(g[i]->is_filtered_guard == (i == 5 || i == 6)); + tt_assert(g[i]->is_usable_filtered_guard == (i == 6)); } + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 1); + + /* Now make sure we have no live consensus, and no nodes. Nothing should + * pass the filter any more. */ + tor_free(dummy_consensus); + dummy_consensus = NULL; + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, node, { + memset(node->identity, 0xff, 20); + }); + entry_guards_update_filtered_sets(gs); + for (i = 0; i < NUM; ++i) { + tt_assert(g[i]->is_filtered_guard == 0); + tt_assert(g[i]->is_usable_filtered_guard == 0); + } + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 0); - /* Inject our lines in the state */ - state_insert_entry_guard_helper(state, entry_state_lines); + done: + guard_selection_free(gs); + tor_free(bl); +#undef NUM +} - /* Parse state */ - retval = entry_guards_parse_state(state, 1, &msg); - tt_int_op(retval, OP_GE, 0); +static void +test_entry_guard_expand_sample(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + digestmap_t *node_by_id = digestmap_new(); + + entry_guard_t *guard = entry_guards_expand_sample(gs); + tt_assert(guard); // the last guard returned. + + // Every sampled guard here should be filtered and reachable for now. + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + /* Make sure we got the right number. */ + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + // Make sure everything we got was from our fake node list, and everything + // was unique. + SMARTLIST_FOREACH_BEGIN(gs->sampled_entry_guards, entry_guard_t *, g) { + const node_t *n = bfn_mock_node_get_by_id(g->identity); + tt_assert(n); + tt_ptr_op(NULL, OP_EQ, digestmap_get(node_by_id, g->identity)); + digestmap_set(node_by_id, g->identity, (void*) n); + int idx = smartlist_pos(big_fake_net_nodes, n); + // The even ones are the guards; make sure we got guards. + tt_int_op(idx & 1, OP_EQ, 0); + } SMARTLIST_FOREACH_END(g); + + // Nothing became unusable/unfiltered, so a subsequent expand should + // make no changes. + guard = entry_guards_expand_sample(gs); + tt_assert(! guard); // no guard was added. + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + // Make a few guards unreachable. + guard = smartlist_get(gs->sampled_entry_guards, 0); + guard->is_usable_filtered_guard = 0; + guard = smartlist_get(gs->sampled_entry_guards, 1); + guard->is_usable_filtered_guard = 0; + guard = smartlist_get(gs->sampled_entry_guards, 2); + guard->is_usable_filtered_guard = 0; + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE - 3, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + // This time, expanding the sample will add some more guards. + guard = entry_guards_expand_sample(gs); + tt_assert(guard); // no guard was added. + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, + num_reachable_filtered_guards(gs, NULL)+3); + + // Still idempotent. + guard = entry_guards_expand_sample(gs); + tt_assert(! guard); // no guard was added. + tt_int_op(DFLT_MIN_FILTERED_SAMPLE_SIZE, OP_EQ, + num_reachable_filtered_guards(gs, NULL)); + + // Now, do a nasty trick: tell the filter to exclude 31/32 of the guards. + // This will cause the sample size to get reeeeally huge, while the + // filtered sample size grows only slowly. + routerset_free(get_options_mutable()->ExcludeNodes); + get_options_mutable()->ExcludeNodes = routerset_new(); + routerset_parse(get_options_mutable()->ExcludeNodes, "144.144.0.0/16", ""); + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, { + if (n_sl_idx % 64 != 0) { + n->rs->addr = 0x90903030; + } + }); + entry_guards_update_filtered_sets(gs); + + // Surely (p ~ 1-2**-60), one of our guards has been excluded. + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_LT, + DFLT_MIN_FILTERED_SAMPLE_SIZE); + + // Try to regenerate the guards. + guard = entry_guards_expand_sample(gs); + tt_assert(guard); // no guard was added. + + /* this time, it's possible that we didn't add enough sampled guards. */ + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_LE, + DFLT_MIN_FILTERED_SAMPLE_SIZE); + /* but we definitely didn't exceed the sample maximum. */ + const int n_guards = 271 / 2; + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_LE, + (int)(n_guards * .3)); - /* Test that the guard was registered. - We need to re-get the entry guard list since its pointer was - overwritten in entry_guards_parse_state(). */ - all_entry_guards = get_entry_guards(); - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 1); + done: + guard_selection_free(gs); + digestmap_free(node_by_id, NULL); +} + +static void +test_entry_guard_expand_sample_small_net(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + /* Fun corner case: not enough guards to make up our whole sample size. */ + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, { + if (n_sl_idx >= 15) { + tor_free(n->rs); + tor_free(n->md); + tor_free(n); + SMARTLIST_DEL_CURRENT(big_fake_net_nodes, n); + } else { + n->rs->addr = 0; // make the filter reject this. + } + }); + + entry_guard_t *guard = entry_guards_expand_sample(gs); + tt_assert(guard); // the last guard returned -- some guard was added. + // half the nodes are guards, so we have 8 guards left. The set + // is small, so we sampled everything. + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, 8); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, 0); + done: + guard_selection_free(gs); +} - { /* Test the entry guard structure */ - char hex_digest[1024]; - char str_time[1024]; +static void +test_entry_guard_update_from_consensus_status(void *arg) +{ + /* Here we're going to have some nodes become un-guardy, and say we got a + * new consensus. This should cause those nodes to get detected as + * unreachable. */ - const entry_guard_t *e = smartlist_get(all_entry_guards, 0); - tt_str_op(e->nickname, OP_EQ, nickname); /* Verify nickname */ + (void)arg; + int i; + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + networkstatus_t *ns_tmp = NULL; + + /* Don't randomly backdate stuff; it will make correctness harder to check.*/ + MOCK(randomize_time, mock_randomize_time_no_randomization); + + /* First, sample some guards. */ + entry_guards_expand_sample(gs); + int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); + tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); + tt_i64_op(n_sampled_pre, OP_GT, 10); + + /* At this point, it should be a no-op to do this: */ + sampled_guards_update_from_consensus(gs); + + /* Now let's make some of our guards become unlisted. The easiest way to + * do that would be to take away their guard flag. */ + for (i = 0; i < 5; ++i) { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 0; + } - base16_encode(hex_digest, sizeof(hex_digest), - e->identity, DIGEST_LEN); - tt_str_op(hex_digest, OP_EQ, fpr); /* Verify fingerprint */ + update_approx_time(start + 30); + { + /* try this with no live networkstatus. Nothing should happen! */ + ns_tmp = dummy_consensus; + dummy_consensus = NULL; + sampled_guards_update_from_consensus(gs); + tt_i64_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); + tt_i64_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre); + /* put the networkstatus back. */ + dummy_consensus = ns_tmp; + ns_tmp = NULL; + } - tt_assert(e->is_dir_cache); /* Verify dirness */ + /* Now those guards should become unlisted, and drop off the filter, but + * stay in the sample. */ + update_approx_time(start + 60); + sampled_guards_update_from_consensus(gs); + + tt_i64_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); + tt_i64_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre-5); + for (i = 0; i < 5; ++i) { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + tt_assert(! g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, start+60); + } + for (i = 5; i < n_sampled_pre; ++i) { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + tt_assert(g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, 0); + } - tt_str_op(e->chosen_by_version, OP_EQ, tor_version); /* Verify version */ + /* Now re-list one, and remove one completely. */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 0); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 1; + } + { + /* try removing the node, to make sure we don't crash on an absent node + */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 5); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + smartlist_remove(big_fake_net_nodes, n); + tor_free(n->rs); + tor_free(n->md); + tor_free(n); + } + update_approx_time(start + 300); + sampled_guards_update_from_consensus(gs); + + /* guards 1..5 are now unlisted; 0,6,7.. are listed. */ + tt_i64_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); + for (i = 1; i < 6; ++i) { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + tt_assert(! g->currently_listed); + if (i == 5) + tt_i64_op(g->unlisted_since_date, OP_EQ, start+300); + else + tt_i64_op(g->unlisted_since_date, OP_EQ, start+60); + } + for (i = 0; i < n_sampled_pre; i = (!i) ? 6 : i+1) { /* 0,6,7,8, ... */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + tt_assert(g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, 0); + } - tt_assert(e->made_contact); /* All saved guards have been contacted */ + done: + tor_free(ns_tmp); /* in case we couldn't put it back */ + guard_selection_free(gs); + UNMOCK(randomize_time); +} - tt_assert(e->bad_since); /* Verify bad_since timestamp */ - format_iso_time(str_time, e->bad_since); - tt_str_op(str_time, OP_EQ, unlisted_since); +static void +test_entry_guard_update_from_consensus_repair(void *arg) +{ + /* Here we'll make sure that our code to repair the unlisted-since + * times is correct. */ - /* The rest should be unset */ - tt_assert(!e->unreachable_since); - tt_assert(!e->can_retry); - tt_assert(!e->path_bias_noticed); - tt_assert(!e->path_bias_warned); - tt_assert(!e->path_bias_extreme); - tt_assert(!e->path_bias_disabled); - tt_assert(!e->path_bias_use_noticed); - tt_assert(!e->path_bias_use_extreme); - tt_assert(!e->last_attempted); + (void)arg; + int i; + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + /* Don't randomly backdate stuff; it will make correctness harder to check.*/ + MOCK(randomize_time, mock_randomize_time_no_randomization); + + /* First, sample some guards. */ + entry_guards_expand_sample(gs); + int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); + tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); + tt_i64_op(n_sampled_pre, OP_GT, 10); + + /* Now corrupt the list a bit. Call some unlisted-since-never, and some + * listed-and-unlisted-since-a-time. */ + update_approx_time(start + 300); + for (i = 0; i < 3; ++i) { + /* these will get a date. */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 0; + g->currently_listed = 0; + } + for (i = 3; i < 6; ++i) { + /* these will become listed. */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + g->unlisted_since_date = start+100; + } + setup_full_capture_of_logs(LOG_WARN); + sampled_guards_update_from_consensus(gs); + expect_log_msg_containing( + "was listed, but with unlisted_since_date set"); + expect_log_msg_containing( + "was unlisted, but with unlisted_since_date unset"); + teardown_capture_of_logs(); + + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre); + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_filtered_pre-3); + for (i = 3; i < n_sampled_pre; ++i) { + /* these will become listed. */ + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, i); + if (i < 3) { + tt_assert(! g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, start+300); + } else { + tt_assert(g->currently_listed); + tt_i64_op(g->unlisted_since_date, OP_EQ, 0); + } } done: - state_lines_free(entry_state_lines); - or_state_free(state); - tor_free(msg); + teardown_capture_of_logs(); + guard_selection_free(gs); + UNMOCK(randomize_time); } -/** Similar to test_entry_guards_parse_state_simple() but aims to test - the PathBias-related details of the entry guard. */ static void -test_entry_guards_parse_state_pathbias(void *arg) +test_entry_guard_update_from_consensus_remove(void *arg) { - or_state_t *state = or_state_new(); - const smartlist_t *all_entry_guards = get_entry_guards(); - char *msg = NULL; - int retval; - smartlist_t *entry_state_lines = smartlist_new(); + /* Now let's check the logic responsible for removing guards from the + * sample entirely. */ - /* Path bias details of the fake guard */ - const double circ_attempts = 9; - const double circ_successes = 8; - const double successful_closed = 4; - const double collapsed = 2; - const double unusable = 0; - const double timeouts = 1; + (void)arg; + //int i; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + smartlist_t *keep_ids = smartlist_new(); + smartlist_t *remove_ids = smartlist_new(); + + /* Don't randomly backdate stuff; it will make correctness harder to check.*/ + MOCK(randomize_time, mock_randomize_time_no_randomization); + + /* First, sample some guards. */ + entry_guards_expand_sample(gs); + int n_sampled_pre = smartlist_len(gs->sampled_entry_guards); + int n_filtered_pre = num_reachable_filtered_guards(gs, NULL); + tt_i64_op(n_sampled_pre, OP_EQ, n_filtered_pre); + tt_i64_op(n_sampled_pre, OP_GT, 10); + + const time_t one_day_ago = approx_time() - 1*24*60*60; + const time_t one_year_ago = approx_time() - 365*24*60*60; + const time_t two_years_ago = approx_time() - 2*365*24*60*60; + /* 0: unlisted for a day. (keep this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 0); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 0; + g->currently_listed = 0; + g->unlisted_since_date = one_day_ago; + smartlist_add(keep_ids, tor_memdup(g->identity, 20)); + } + /* 1: unlisted for a year. (remove this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 1); + node_t *n = (node_t*) bfn_mock_node_get_by_id(g->identity); + n->is_possible_guard = 0; + g->currently_listed = 0; + g->unlisted_since_date = one_year_ago; + smartlist_add(remove_ids, tor_memdup(g->identity, 20)); + } + /* 2: added a day ago, never confirmed. (keep this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 2); + g->sampled_on_date = one_day_ago; + smartlist_add(keep_ids, tor_memdup(g->identity, 20)); + } + /* 3: added a year ago, never confirmed. (remove this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 3); + g->sampled_on_date = one_year_ago; + smartlist_add(remove_ids, tor_memdup(g->identity, 20)); + } + /* 4: added two year ago, confirmed yesterday, primary. (keep this.) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 4); + g->sampled_on_date = one_year_ago; + g->confirmed_on_date = one_day_ago; + g->confirmed_idx = 0; + g->is_primary = 1; + smartlist_add(gs->confirmed_entry_guards, g); + smartlist_add(gs->primary_entry_guards, g); + smartlist_add(keep_ids, tor_memdup(g->identity, 20)); + } + /* 5: added two years ago, confirmed a year ago, primary. (remove this) */ + { + entry_guard_t *g = smartlist_get(gs->sampled_entry_guards, 5); + g->sampled_on_date = two_years_ago; + g->confirmed_on_date = one_year_ago; + g->confirmed_idx = 1; + g->is_primary = 1; + smartlist_add(gs->confirmed_entry_guards, g); + smartlist_add(gs->primary_entry_guards, g); + smartlist_add(remove_ids, tor_memdup(g->identity, 20)); + } - (void) arg; + sampled_guards_update_from_consensus(gs); - /* The global entry guards smartlist should be empty now. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 0); + /* Did we remove the right ones? */ + SMARTLIST_FOREACH(keep_ids, uint8_t *, id, { + tt_assert(get_sampled_guard_with_id(gs, id) != NULL); + }); + SMARTLIST_FOREACH(remove_ids, uint8_t *, id, { + tt_want(get_sampled_guard_with_id(gs, id) == NULL); + }); - { /* Prepare the state entry */ + /* Did we remove the right number? */ + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_sampled_pre - 3); - /* Prepare the smartlist to hold the key/value of each line */ - smartlist_t *state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuard"); - smartlist_add_asprintf(state_line, - "givethanks B29D536DD1752D542E1FBB3C9CE4449D51298212 NoDirCache"); - smartlist_add(entry_state_lines, state_line); + done: + guard_selection_free(gs); + UNMOCK(randomize_time); + SMARTLIST_FOREACH(keep_ids, char *, cp, tor_free(cp)); + SMARTLIST_FOREACH(remove_ids, char *, cp, tor_free(cp)); + smartlist_free(keep_ids); + smartlist_free(remove_ids); +} - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardAddedBy"); - smartlist_add_asprintf(state_line, - "B29D536DD1752D542E1FBB3C9CE4449D51298212 0.2.5.3-alpha-dev " - "%s", get_yesterday_date_str()); - smartlist_add(entry_state_lines, state_line); +static void +test_entry_guard_confirming_guards(void *arg) +{ + (void)arg; + /* Now let's check the logic responsible for manipulating the list + * of confirmed guards */ + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + MOCK(randomize_time, mock_randomize_time_no_randomization); + + /* Create the sample. */ + entry_guards_expand_sample(gs); + + /* Confirm a few guards. */ + time_t start = approx_time(); + entry_guard_t *g1 = smartlist_get(gs->sampled_entry_guards, 0); + entry_guard_t *g2 = smartlist_get(gs->sampled_entry_guards, 1); + entry_guard_t *g3 = smartlist_get(gs->sampled_entry_guards, 8); + make_guard_confirmed(gs, g2); + update_approx_time(start + 10); + make_guard_confirmed(gs, g1); + make_guard_confirmed(gs, g3); + + /* Were the correct dates and indices fed in? */ + tt_int_op(g1->confirmed_idx, OP_EQ, 1); + tt_int_op(g2->confirmed_idx, OP_EQ, 0); + tt_int_op(g3->confirmed_idx, OP_EQ, 2); + tt_i64_op(g1->confirmed_on_date, OP_EQ, start+10); + tt_i64_op(g2->confirmed_on_date, OP_EQ, start); + tt_i64_op(g3->confirmed_on_date, OP_EQ, start+10); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 0), OP_EQ, g2); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 1), OP_EQ, g1); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 2), OP_EQ, g3); + + /* Now make sure we can regenerate the confirmed_entry_guards list. */ + smartlist_clear(gs->confirmed_entry_guards); + g2->confirmed_idx = 0; + g1->confirmed_idx = 10; + g3->confirmed_idx = 100; + entry_guards_update_confirmed(gs); + tt_int_op(g1->confirmed_idx, OP_EQ, 1); + tt_int_op(g2->confirmed_idx, OP_EQ, 0); + tt_int_op(g3->confirmed_idx, OP_EQ, 2); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 0), OP_EQ, g2); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 1), OP_EQ, g1); + tt_ptr_op(smartlist_get(gs->confirmed_entry_guards, 2), OP_EQ, g3); + + /* Now make sure we can regenerate the confirmed_entry_guards list if + * the indices are messed up. */ + g1->confirmed_idx = g2->confirmed_idx = g3->confirmed_idx = 999; + smartlist_clear(gs->confirmed_entry_guards); + entry_guards_update_confirmed(gs); + tt_int_op(g1->confirmed_idx, OP_GE, 0); + tt_int_op(g2->confirmed_idx, OP_GE, 0); + tt_int_op(g3->confirmed_idx, OP_GE, 0); + tt_int_op(g1->confirmed_idx, OP_LE, 2); + tt_int_op(g2->confirmed_idx, OP_LE, 2); + tt_int_op(g3->confirmed_idx, OP_LE, 2); + g1 = smartlist_get(gs->confirmed_entry_guards, 0); + g2 = smartlist_get(gs->confirmed_entry_guards, 1); + g3 = smartlist_get(gs->confirmed_entry_guards, 2); + tt_int_op(g1->confirmed_idx, OP_EQ, 0); + tt_int_op(g2->confirmed_idx, OP_EQ, 1); + tt_int_op(g3->confirmed_idx, OP_EQ, 2); + tt_assert(g1 != g2); + tt_assert(g1 != g3); + tt_assert(g2 != g3); - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardUnlistedSince"); - smartlist_add_asprintf(state_line, "2014-06-08 16:16:50"); - smartlist_add(entry_state_lines, state_line); + done: + UNMOCK(randomize_time); + guard_selection_free(gs); +} - state_line = smartlist_new(); - smartlist_add_asprintf(state_line, "EntryGuardPathBias"); - smartlist_add_asprintf(state_line, "%f %f %f %f %f %f", - circ_attempts, circ_successes, successful_closed, - collapsed, unusable, timeouts); - smartlist_add(entry_state_lines, state_line); +static void +test_entry_guard_sample_reachable_filtered(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + entry_guards_expand_sample(gs); + const int N = 10000; + bitarray_t *selected = NULL; + int i, j; + + /* We've got a sampled list now; let's make one non-usable-filtered; some + * confirmed, some primary, some pending. + */ + int n_guards = smartlist_len(gs->sampled_entry_guards); + tt_int_op(n_guards, OP_GT, 10); + entry_guard_t *g; + g = smartlist_get(gs->sampled_entry_guards, 0); + g->is_pending = 1; + g = smartlist_get(gs->sampled_entry_guards, 1); + make_guard_confirmed(gs, g); + g = smartlist_get(gs->sampled_entry_guards, 2); + g->is_primary = 1; + g = smartlist_get(gs->sampled_entry_guards, 3); + g->pb.path_bias_disabled = 1; + + entry_guards_update_filtered_sets(gs); + gs->primary_guards_up_to_date = 1; + tt_int_op(num_reachable_filtered_guards(gs, NULL), OP_EQ, n_guards - 1); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_guards); + + // +1 since the one we made disabled will make another one get added. + ++n_guards; + + /* Try a bunch of selections. */ + const struct { + int flag; int idx; + } tests[] = { + { 0, -1 }, + { SAMPLE_EXCLUDE_CONFIRMED, 1 }, + { SAMPLE_EXCLUDE_PRIMARY|SAMPLE_NO_UPDATE_PRIMARY, 2 }, + { SAMPLE_EXCLUDE_PENDING, 0 }, + { -1, -1}, + }; + + for (j = 0; tests[j].flag >= 0; ++j) { + selected = bitarray_init_zero(n_guards); + const int excluded_flags = tests[j].flag; + const int excluded_idx = tests[j].idx; + for (i = 0; i < N; ++i) { + g = sample_reachable_filtered_entry_guards(gs, NULL, excluded_flags); + tor_assert(g); + int pos = smartlist_pos(gs->sampled_entry_guards, g); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, n_guards); + tt_int_op(pos, OP_GE, 0); + tt_int_op(pos, OP_LT, n_guards); + bitarray_set(selected, pos); + } + for (i = 0; i < n_guards; ++i) { + const int should_be_set = (i != excluded_idx && + i != 3); // filtered out. + tt_int_op(!!bitarray_is_set(selected, i), OP_EQ, should_be_set); + } + bitarray_free(selected); + selected = NULL; } - /* Inject our lines in the state */ - state_insert_entry_guard_helper(state, entry_state_lines); + done: + guard_selection_free(gs); + bitarray_free(selected); +} + +static void +test_entry_guard_sample_reachable_filtered_empty(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + /* What if we try to sample from a set of 0? */ + SMARTLIST_FOREACH(big_fake_net_nodes, node_t *, n, + n->is_possible_guard = 0); - /* Parse state */ - retval = entry_guards_parse_state(state, 1, &msg); - tt_int_op(retval, OP_GE, 0); + entry_guard_t *g = sample_reachable_filtered_entry_guards(gs, NULL, 0); + tt_ptr_op(g, OP_EQ, NULL); - /* Test that the guard was registered */ - all_entry_guards = get_entry_guards(); - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 1); + done: + guard_selection_free(gs); +} - { /* Test the path bias of this guard */ - const entry_guard_t *e = smartlist_get(all_entry_guards, 0); +static void +test_entry_guard_retry_unreachable(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + entry_guards_expand_sample(gs); + /* Let's say that we have two guards, and they're down. + */ + time_t start = approx_time();; + entry_guard_t *g1 = smartlist_get(gs->sampled_entry_guards, 0); + entry_guard_t *g2 = smartlist_get(gs->sampled_entry_guards, 1); + entry_guard_t *g3 = smartlist_get(gs->sampled_entry_guards, 2); + g1->is_reachable = GUARD_REACHABLE_NO; + g2->is_reachable = GUARD_REACHABLE_NO; + g1->is_primary = 1; + g1->failing_since = g2->failing_since = start; + g1->last_tried_to_connect = g2->last_tried_to_connect = start; + + /* Wait 5 minutes. Nothing will get retried. */ + update_approx_time(start + 5 * 60); + entry_guard_consider_retry(g1); + entry_guard_consider_retry(g2); + entry_guard_consider_retry(g3); // just to make sure this doesn't crash. + tt_int_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_int_op(g3->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + /* After 30 min, the primary one gets retried */ + update_approx_time(start + 35 * 60); + entry_guard_consider_retry(g1); + entry_guard_consider_retry(g2); + tt_int_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + + g1->is_reachable = GUARD_REACHABLE_NO; + g1->last_tried_to_connect = start + 55*60; + + /* After 1 hour, we'll retry the nonprimary one. */ + update_approx_time(start + 61 * 60); + entry_guard_consider_retry(g1); + entry_guard_consider_retry(g2); + tt_int_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + g2->is_reachable = GUARD_REACHABLE_NO; + g2->last_tried_to_connect = start + 61*60; + + /* And then the primary one again. */ + update_approx_time(start + 66 * 60); + entry_guard_consider_retry(g1); + entry_guard_consider_retry(g2); + tt_int_op(g1->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_NO); - tt_assert(!e->is_dir_cache); - tt_assert(!e->can_retry); + done: + guard_selection_free(gs); +} - /* XXX tt_double_op doesn't support equality. Cast to int for now. */ - tt_int_op((int)e->circ_attempts, OP_EQ, (int)circ_attempts); - tt_int_op((int)e->circ_successes, OP_EQ, (int)circ_successes); - tt_int_op((int)e->successful_circuits_closed, OP_EQ, - (int)successful_closed); - tt_int_op((int)e->timeouts, OP_EQ, (int)timeouts); - tt_int_op((int)e->collapsed_circuits, OP_EQ, (int)collapsed); - tt_int_op((int)e->unusable_circuits, OP_EQ, (int)unusable); +static void +test_entry_guard_manage_primary(void *arg) +{ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + smartlist_t *prev_guards = smartlist_new(); + + /* If no guards are confirmed, we should pick a few reachable guards and + * call them all primary. But not confirmed.*/ + entry_guards_update_primary(gs); + int n_primary = smartlist_len(gs->primary_entry_guards); + tt_int_op(n_primary, OP_GE, 1); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { + tt_assert(g->is_primary); + tt_assert(g->confirmed_idx == -1); + }); + + /* Calling it a second time should leave the guards unchanged. */ + smartlist_add_all(prev_guards, gs->primary_entry_guards); + entry_guards_update_primary(gs); + tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, n_primary); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { + tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx)); + }); + + /* If we have one confirmed guard, that guards becomes the first primary + * guard, and the other primary guards get kept. */ + + /* find a non-primary guard... */ + entry_guard_t *confirmed = NULL; + SMARTLIST_FOREACH(gs->sampled_entry_guards, entry_guard_t *, g, { + if (! g->is_primary) { + confirmed = g; + break; + } + }); + tt_assert(confirmed); + /* make it confirmed. */ + make_guard_confirmed(gs, confirmed); + /* update the list... */ + smartlist_clear(prev_guards); + smartlist_add_all(prev_guards, gs->primary_entry_guards); + entry_guards_update_primary(gs); + + /* and see what's primary now! */ + tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, n_primary); + tt_ptr_op(smartlist_get(gs->primary_entry_guards, 0), OP_EQ, confirmed); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { + tt_assert(g->is_primary); + if (g_sl_idx == 0) + continue; + tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx - 1)); + }); + { + entry_guard_t *prev_last_guard = smartlist_get(prev_guards, n_primary-1); + tt_assert(! prev_last_guard->is_primary); } + /* Calling it a fourth time should leave the guards unchanged. */ + smartlist_clear(prev_guards); + smartlist_add_all(prev_guards, gs->primary_entry_guards); + entry_guards_update_primary(gs); + tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, n_primary); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, g, { + tt_ptr_op(g, OP_EQ, smartlist_get(prev_guards, g_sl_idx)); + }); + done: - or_state_free(state); - state_lines_free(entry_state_lines); - tor_free(msg); + guard_selection_free(gs); + smartlist_free(prev_guards); } -/* Simple test of entry_guards_set_from_config() by specifying a - particular EntryNode and making sure it gets picked. */ static void -test_entry_guards_set_from_config(void *arg) +test_entry_guard_guard_preferred(void *arg) { - or_options_t *options = get_options_mutable(); - const smartlist_t *all_entry_guards = get_entry_guards(); - const char *entrynodes_str = "test003r"; - const node_t *chosen_entry = NULL; - int retval; - (void) arg; + entry_guard_t *g1 = tor_malloc_zero(sizeof(entry_guard_t)); + entry_guard_t *g2 = tor_malloc_zero(sizeof(entry_guard_t)); + + g1->confirmed_idx = g2->confirmed_idx = -1; + g1->last_tried_to_connect = approx_time(); + g2->last_tried_to_connect = approx_time(); + + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g1)); - /* Prase EntryNodes as a routerset. */ - options->EntryNodes = routerset_new(); - retval = routerset_parse(options->EntryNodes, - entrynodes_str, - "test_entrynodes"); - tt_int_op(retval, OP_GE, 0); + /* Neither is pending; priorities equal. */ + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g2)); - /* Read nodes from EntryNodes */ - entry_guards_set_from_config(options); + /* If one is pending, the pending one has higher priority */ + g1->is_pending = 1; + tt_int_op(1, OP_EQ, entry_guard_has_higher_priority(g1, g2)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g2, g1)); - /* Test that only one guard was added. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 1); + /* If both are pending, and last_tried_to_connect is equal: + priorities equal */ + g2->is_pending = 1; + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g2)); - /* Make sure it was the guard we specified. */ - chosen_entry = choose_random_entry(NULL); - tt_str_op(chosen_entry->ri->nickname, OP_EQ, entrynodes_str); + /* One had a connection that startied earlier: it has higher priority. */ + g2->last_tried_to_connect -= 10; + tt_int_op(1, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g2)); + + /* Now, say that g1 is confirmed. It will get higher priority. */ + g1->confirmed_idx = 5; + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(1, OP_EQ, entry_guard_has_higher_priority(g1, g2)); + + /* But if g2 was confirmed first, it will get priority */ + g2->confirmed_idx = 2; + tt_int_op(1, OP_EQ, entry_guard_has_higher_priority(g2, g1)); + tt_int_op(0, OP_EQ, entry_guard_has_higher_priority(g1, g2)); done: - routerset_free(options->EntryNodes); + tor_free(g1); + tor_free(g2); } static void -test_entry_is_time_to_retry(void *arg) +test_entry_guard_select_for_circuit_no_confirmed(void *arg) { - entry_guard_t *test_guard; - time_t now; - int retval; + /* Simpler cases: no gaurds are confirmed yet. */ (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + /* simple starting configuration */ + entry_guards_update_primary(gs); + unsigned state = 9999; + + entry_guard_t *g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, + NULL, &state); + + tt_assert(g); + tt_assert(g->is_primary); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + tt_assert(g->is_pending == 0); // primary implies non-pending. + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time()); + + // If we do that again, we should get the same guard. + entry_guard_t *g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, + NULL, &state); + tt_ptr_op(g2, OP_EQ, g); + + // if we mark that guard down, we should get a different primary guard. + // auto-retry it. + g->is_reachable = GUARD_REACHABLE_NO; + g->failing_since = approx_time() - 10; + g->last_tried_to_connect = approx_time() - 10; + state = 9999; + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g2, OP_NE, g); + tt_assert(g2); + tt_assert(g2->is_primary); + tt_int_op(g2->confirmed_idx, OP_EQ, -1); + tt_assert(g2->is_pending == 0); // primary implies non-pending. + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time()); + + // If we say that the first primary guard was last tried a long time ago, we + // should get an automatic retry on it. + g->failing_since = approx_time() - 72*60*60; + g->last_tried_to_connect = approx_time() - 72*60*60; + state = 9999; + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g2, OP_EQ, g); + tt_assert(g2); + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time()); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + // And if we mark ALL the primary guards down, we should get another guard + // at random. + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, guard, { + guard->is_reachable = GUARD_REACHABLE_NO; + guard->last_tried_to_connect = approx_time() - 5; + guard->failing_since = approx_time() - 30; + }); + state = 9999; + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_assert(g2); + tt_assert(!g2->is_primary); + tt_int_op(g2->confirmed_idx, OP_EQ, -1); + tt_assert(g2->is_pending == 1); + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time()); + tt_int_op(g2->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + + // As a bonus, maybe we should be retrying the primary guards. Let's say so. + mark_primary_guards_maybe_reachable(gs); + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, guard, { + tt_int_op(guard->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + tt_assert(guard->is_usable_filtered_guard == 1); + // no change to these fields. + tt_i64_op(guard->last_tried_to_connect, OP_EQ, approx_time() - 5); + tt_i64_op(guard->failing_since, OP_EQ, approx_time() - 30); + }); + + /* Let's try again and we should get the first primary guard again */ + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g, OP_EQ, smartlist_get(gs->primary_entry_guards, 0)); + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_ptr_op(g2, OP_EQ, g); + + /* But if we impose a restriction, we don't get the same guard */ + entry_guard_restriction_t rst; + memset(&rst, 0, sizeof(rst)); + memcpy(rst.exclude_id, g->identity, DIGEST_LEN); + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); + tt_ptr_op(g2, OP_NE, g); - now = time(NULL); + done: + guard_selection_free(gs); +} - test_guard = tor_malloc_zero(sizeof(entry_guard_t)); +static void +test_entry_guard_select_for_circuit_confirmed(void *arg) +{ + /* Case 2: if all the primary guards are down, and there are more confirmed + guards, we use a confirmed guard. */ + (void)arg; + int i; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + const int N_CONFIRMED = 10; + + /* slightly more complicated simple starting configuration */ + entry_guards_update_primary(gs); + for (i = 0; i < N_CONFIRMED; ++i) { + entry_guard_t *guard = smartlist_get(gs->sampled_entry_guards, i); + make_guard_confirmed(gs, guard); + } + entry_guards_update_primary(gs); // rebuild the primary list. + + unsigned state = 9999; + + // As above, this gives us a primary guard. + entry_guard_t *g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, + NULL, &state); + tt_assert(g); + tt_assert(g->is_primary); + tt_int_op(g->confirmed_idx, OP_EQ, 0); + tt_assert(g->is_pending == 0); // primary implies non-pending. + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time()); + tt_ptr_op(g, OP_EQ, smartlist_get(gs->primary_entry_guards, 0)); + + // But if we mark all the primary guards down... + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, guard, { + guard->last_tried_to_connect = approx_time(); + entry_guards_note_guard_failure(gs, guard); + }); + + // ... we should get a confirmed guard. + state = 9999; + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_assert(g); + tt_assert(! g->is_primary); + tt_int_op(g->confirmed_idx, OP_EQ, smartlist_len(gs->primary_entry_guards)); + tt_assert(g->is_pending); + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_i64_op(g->last_tried_to_connect, OP_EQ, approx_time()); + + // And if we try again, we should get a different confirmed guard, since + // that one is pending. + state = 9999; + entry_guard_t *g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, + NULL, &state); + tt_assert(g2); + tt_assert(! g2->is_primary); + tt_ptr_op(g2, OP_NE, g); + tt_int_op(g2->confirmed_idx, OP_EQ, + smartlist_len(gs->primary_entry_guards)+1); + tt_assert(g2->is_pending); + tt_uint_op(state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_i64_op(g2->last_tried_to_connect, OP_EQ, approx_time()); + + // If we say that the next confirmed guard in order is excluded, we get + // The one AFTER that. + g = smartlist_get(gs->confirmed_entry_guards, + smartlist_len(gs->primary_entry_guards)+2); + entry_guard_restriction_t rst; + memset(&rst, 0, sizeof(rst)); + memcpy(rst.exclude_id, g->identity, DIGEST_LEN); + g2 = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, &rst, &state); + tt_ptr_op(g2, OP_NE, g); + tt_int_op(g2->confirmed_idx, OP_EQ, + smartlist_len(gs->primary_entry_guards)+3); + + // If we make every confirmed guard become pending then we start poking + // other guards. + const int n_remaining_confirmed = + N_CONFIRMED - 3 - smartlist_len(gs->primary_entry_guards); + for (i = 0; i < n_remaining_confirmed; ++i) { + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_int_op(g->confirmed_idx, OP_GE, 0); + tt_assert(g); + } + state = 9999; + g = select_entry_guard_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &state); + tt_assert(g); + tt_assert(g->is_pending); + tt_int_op(g->confirmed_idx, OP_EQ, -1); - test_guard->last_attempted = now - 10; - test_guard->unreachable_since = now - 1; + done: + guard_selection_free(gs); +} - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); +static void +test_entry_guard_select_for_circuit_highlevel_primary(void *arg) +{ + /* Play around with selecting primary guards for circuits and markign + * them up and down */ + (void)arg; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + + time_t start = approx_time(); + + const node_t *node = NULL; + circuit_guard_state_t *guard = NULL; + entry_guard_t *g; + guard_usable_t u; + /* + * Make sure that the pick-for-circuit API basically works. We'll get + * a primary guard, so it'll be usable on completion. + */ + int r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + + /* Call that circuit successful. */ + update_approx_time(start+15); + u = entry_guard_succeeded(&guard); + tt_int_op(u, OP_EQ, GUARD_USABLE_NOW); /* We can use it now. */ + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_YES); + tt_int_op(g->confirmed_idx, OP_EQ, 0); + + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + g = NULL; + + /* Try again. We'll also get a primary guard this time. (The same one, + in fact.) But this time, we'll say the connection has failed. */ + update_approx_time(start+35); + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + tt_i64_op(guard->state_set_at, OP_EQ, start+35); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start+35); + tt_int_op(g->confirmed_idx, OP_EQ, 0); // same one. + + /* It's failed! What will happen to our poor guard? */ + update_approx_time(start+45); + entry_guard_failed(&guard); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_DEAD); + tt_i64_op(guard->state_set_at, OP_EQ, start+45); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + tt_i64_op(g->failing_since, OP_EQ, start+45); + tt_int_op(g->confirmed_idx, OP_EQ, 0); // still confirmed. + + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + entry_guard_t *g_prev = g; + g = NULL; + + /* Now try a third time. Since the other one is down, we'll get a different + * (still primary) guard. + */ + update_approx_time(start+60); + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(r == 0); + tt_assert(node); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_ptr_op(g, OP_NE, g_prev); + tt_mem_op(g->identity, OP_EQ, node->identity, DIGEST_LEN); + tt_mem_op(g->identity, OP_NE, g_prev->identity, DIGEST_LEN); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_i64_op(g->last_tried_to_connect, OP_EQ, start+60); + tt_int_op(g->confirmed_idx, OP_EQ, -1); // not confirmd now. + + /* Call this one up; watch it get confirmed. */ + update_approx_time(start+90); + u = entry_guard_succeeded(&guard); + tt_int_op(u, OP_EQ, GUARD_USABLE_NOW); + tt_assert(guard); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_YES); + tt_int_op(g->confirmed_idx, OP_EQ, 1); - test_guard->unreachable_since = now - (6*60*60 - 1); - test_guard->last_attempted = now - (60*60 + 1); + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); +} - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); +static void +test_entry_guard_select_for_circuit_highlevel_confirm_other(void *arg) +{ + (void) arg; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + + /* At the start, we have no confirmed guards. We'll mark the primary guards + * down, then confirm something else. As soon as we do, it should become + * primary, and we should get it next time. */ + + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + circuit_guard_state_t *guard = NULL; + int i, r; + const node_t *node = NULL; + guard_usable_t u; + + /* Declare that we're on the internet. */ + entry_guards_note_internet_connectivity(gs); + + /* Primary guards are down! */ + for (i = 0; i < N_PRIMARY; ++i) { + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + entry_guard_failed(&guard); + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + } - test_guard->last_attempted = now - (60*60 - 1); + /* Next guard should be non-primary. */ + node = NULL; + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + entry_guard_t *g = entry_guard_handle_get(guard->guard); + tt_assert(g); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tt_int_op(g->confirmed_idx, OP_EQ, -1); + tt_int_op(g->is_primary, OP_EQ, 0); + tt_int_op(g->is_pending, OP_EQ, 1); + (void)start; + + u = entry_guard_succeeded(&guard); + /* We're on the internet (by fiat), so this guard will get called "confirmed" + * and should immediately become primary. + */ + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + tt_assert(u == GUARD_USABLE_NOW); + tt_int_op(g->confirmed_idx, OP_EQ, 0); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_int_op(g->is_pending, OP_EQ, 0); - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,0); + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); +} - test_guard->unreachable_since = now - (6*60*60 + 1); - test_guard->last_attempted = now - (4*60*60 + 1); +static void +test_entry_guard_select_for_circuit_highlevel_primary_retry(void *arg) +{ + (void) arg; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + + /* At the start, we have no confirmed guards. We'll mark the primary guards + * down, then confirm something else. As soon as we do, it should become + * primary, and we should get it next time. */ + + time_t start = approx_time(); + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + circuit_guard_state_t *guard = NULL, *guard2 = NULL; + int i, r; + const node_t *node = NULL; + entry_guard_t *g; + guard_usable_t u; + + /* Declare that we're on the internet. */ + entry_guards_note_internet_connectivity(gs); + + /* Make primary guards confirmed (so they won't be superseded by a later + * guard), then mark them down. */ + for (i = 0; i < N_PRIMARY; ++i) { + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + make_guard_confirmed(gs, g); + tt_int_op(g->is_primary, OP_EQ, 1); + entry_guard_failed(&guard); + circuit_guard_state_free(guard); + tt_int_op(g->is_reachable, OP_EQ, GUARD_REACHABLE_NO); + guard = NULL; + node = NULL; + } - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + /* Get another guard that we might try. */ + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + g = entry_guard_handle_get(guard->guard); + tt_int_op(g->is_primary, OP_EQ, 0); + + tt_assert(entry_guards_all_primary_guards_are_down(gs)); + + /* And an hour has passed ... */ + update_approx_time(start + 3600); + + /* Say that guard has succeeded! */ + u = entry_guard_succeeded(&guard); + tt_int_op(u, OP_EQ, GUARD_MAYBE_USABLE_LATER); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD); + g = entry_guard_handle_get(guard->guard); + + /* The primary guards should have been marked up! */ + SMARTLIST_FOREACH(gs->primary_entry_guards, entry_guard_t *, pg, { + tt_int_op(pg->is_primary, OP_EQ, 1); + tt_ptr_op(g, OP_NE, pg); + tt_int_op(pg->is_reachable, OP_EQ, GUARD_REACHABLE_MAYBE); + }); + + /* Have a circuit to a primary guard succeed. */ + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard2); + tt_assert(r == 0); + tt_int_op(guard2->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + u = entry_guard_succeeded(&guard2); + tt_assert(u == GUARD_USABLE_NOW); + tt_int_op(guard2->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + + tt_assert(! entry_guards_all_primary_guards_are_down(gs)); - test_guard->unreachable_since = now - (3*24*60*60 - 1); - test_guard->last_attempted = now - (4*60*60 + 1); + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); + circuit_guard_state_free(guard2); +} - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); +static void +test_entry_guard_select_and_cancel(void *arg) +{ + (void) arg; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + int i,r; + const node_t *node = NULL; + circuit_guard_state_t *guard; + guard_selection_t *gs = guard_selection_new("default", GS_TYPE_NORMAL); + entry_guard_t *g; + + /* Once more, we mark all the primary guards down. */ + entry_guards_note_internet_connectivity(gs); + for (i = 0; i < N_PRIMARY; ++i) { + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_int_op(r, OP_EQ, 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_ON_COMPLETION); + g = entry_guard_handle_get(guard->guard); + tt_int_op(g->is_primary, OP_EQ, 1); + tt_int_op(g->is_pending, OP_EQ, 0); + make_guard_confirmed(gs, g); + entry_guard_failed(&guard); + circuit_guard_state_free(guard); + guard = NULL; + node = NULL; + } - test_guard->unreachable_since = now - (3*24*60*60 + 1); - test_guard->last_attempted = now - (18*60*60 + 1); + tt_assert(entry_guards_all_primary_guards_are_down(gs)); + + /* Now get another guard we could try... */ + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_assert(node); + tt_assert(guard); + tt_assert(r == 0); + tt_int_op(guard->state, OP_EQ, GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + g = entry_guard_handle_get(guard->guard); + tt_int_op(g->is_primary, OP_EQ, 0); + tt_int_op(g->is_pending, OP_EQ, 1); + + /* Whoops! We should never have asked for this guard. Cancel the request! */ + entry_guard_cancel(&guard); + tt_assert(guard == NULL); + tt_int_op(g->is_primary, OP_EQ, 0); + tt_int_op(g->is_pending, OP_EQ, 0); - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + done: + guard_selection_free(gs); + circuit_guard_state_free(guard); +} - test_guard->unreachable_since = now - (7*24*60*60 - 1); - test_guard->last_attempted = now - (18*60*60 + 1); +static void +test_entry_guard_drop_guards(void *arg) +{ + (void) arg; + int r; + const node_t *node = NULL; + circuit_guard_state_t *guard; + guard_selection_t *gs = get_guard_selection_info(); + + // Pick a guard, to get things set up. + r = entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &guard); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_GE, + DFLT_MIN_FILTERED_SAMPLE_SIZE); + tt_ptr_op(gs, OP_EQ, get_guard_selection_info()); + + // Drop all the guards! (This is a bad idea....) + remove_all_entry_guards_for_guard_selection(gs); + gs = get_guard_selection_info(); + tt_int_op(smartlist_len(gs->sampled_entry_guards), OP_EQ, 0); + tt_int_op(smartlist_len(gs->primary_entry_guards), OP_EQ, 0); + tt_int_op(smartlist_len(gs->confirmed_entry_guards), OP_EQ, 0); - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + done: + circuit_guard_state_free(guard); + guard_selection_free(gs); +} - test_guard->last_attempted = now - (18*60*60 - 1); +/* Unit test setup function: Create a fake network, and set everything up + * for testing the upgrade-a-waiting-circuit code. */ +typedef struct { + guard_selection_t *gs; + time_t start; + circuit_guard_state_t *guard1_state; + circuit_guard_state_t *guard2_state; + entry_guard_t *guard1; + entry_guard_t *guard2; + origin_circuit_t *circ1; + origin_circuit_t *circ2; + smartlist_t *all_origin_circuits; +} upgrade_circuits_data_t; +static void * +upgrade_circuits_setup(const struct testcase_t *testcase) +{ + upgrade_circuits_data_t *data = tor_malloc_zero(sizeof(*data)); + guard_selection_t *gs = data->gs = + guard_selection_new("default", GS_TYPE_NORMAL); + circuit_guard_state_t *guard; + const node_t *node; + entry_guard_t *g; + int i; + const int N_PRIMARY = DFLT_N_PRIMARY_GUARDS; + const char *argument = testcase->setup_data; + const int make_circ1_succeed = strstr(argument, "c1-done") != NULL; + const int make_circ2_succeed = strstr(argument, "c2-done") != NULL; + + big_fake_network_setup(testcase); + + /* We're going to set things up in a state where a circuit will be ready to + * be upgraded. Each test can make a single change (or not) that should + * block the upgrade. + */ + + /* First, make all the primary guards confirmed, and down. */ + data->start = approx_time(); + entry_guards_note_internet_connectivity(gs); + for (i = 0; i < N_PRIMARY; ++i) { + entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, &node, &guard); + g = entry_guard_handle_get(guard->guard); + make_guard_confirmed(gs, g); + entry_guard_failed(&guard); + circuit_guard_state_free(guard); + } - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,0); + /* Grab another couple of guards */ + data->all_origin_circuits = smartlist_new(); + + update_approx_time(data->start + 27); + entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &data->guard1_state); + origin_circuit_t *circ; + data->circ1 = circ = origin_circuit_new(); + circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; + circ->guard_state = data->guard1_state; + smartlist_add(data->all_origin_circuits, circ); + + update_approx_time(data->start + 30); + entry_guard_pick_for_circuit(gs, GUARD_USAGE_TRAFFIC, NULL, + &node, &data->guard2_state); + data->circ2 = circ = origin_circuit_new(); + circ->base_.purpose = CIRCUIT_PURPOSE_C_GENERAL; + circ->guard_state = data->guard2_state; + smartlist_add(data->all_origin_circuits, circ); + + data->guard1 = entry_guard_handle_get(data->guard1_state->guard); + data->guard2 = entry_guard_handle_get(data->guard2_state->guard); + tor_assert(data->guard1 != data->guard2); + tor_assert(data->guard1_state->state == + GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + tor_assert(data->guard2_state->state == + GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD); + + guard_usable_t r; + update_approx_time(data->start + 32); + if (make_circ1_succeed) { + r = entry_guard_succeeded(&data->guard1_state); + tor_assert(r == GUARD_MAYBE_USABLE_LATER); + tor_assert(data->guard1_state->state == + GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD); + } + update_approx_time(data->start + 33); + if (make_circ2_succeed) { + r = entry_guard_succeeded(&data->guard2_state); + tor_assert(r == GUARD_MAYBE_USABLE_LATER); + tor_assert(data->guard2_state->state == + GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD); + } - test_guard->unreachable_since = now - (7*24*60*60 + 1); - test_guard->last_attempted = now - (36*60*60 + 1); + return data; +} +static int +upgrade_circuits_cleanup(const struct testcase_t *testcase, void *ptr) +{ + upgrade_circuits_data_t *data = ptr; + // circuit_guard_state_free(data->guard1_state); // held in circ1 + // circuit_guard_state_free(data->guard2_state); // held in circ2 + guard_selection_free(data->gs); + smartlist_free(data->all_origin_circuits); + circuit_free(TO_CIRCUIT(data->circ1)); + circuit_free(TO_CIRCUIT(data->circ2)); + tor_free(data); + return big_fake_network_cleanup(testcase, NULL); +} + +static void +test_entry_guard_upgrade_a_circuit(void *arg) +{ + upgrade_circuits_data_t *data = arg; - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + /* This is the easy case: we have no COMPLETED circuits, all the + * primary guards are down, we have two WAITING circuits: one will + * get upgraded to COMPLETED! (The one that started first.) + */ - test_guard->unreachable_since = now - (7*24*60*60 + 1); - test_guard->last_attempted = now - (36*60*60 + 1); + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); - retval = entry_is_time_to_retry(test_guard,now); - tt_int_op(retval,OP_EQ,1); + /* circ1 was started first, so we'll get told to ugrade it... */ + tt_ptr_op(oc, OP_EQ, data->circ1); + + /* And the guard state should be complete */ + tt_ptr_op(data->guard1_state, OP_NE, NULL); + tt_int_op(data->guard1_state->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); done: - tor_free(test_guard); + smartlist_free(result); } -/** XXX Do some tests that entry_is_live() */ static void -test_entry_is_live(void *arg) +test_entry_guard_upgrade_blocked_by_live_primary_guards(void *arg) { - smartlist_t *our_nodelist = NULL; - const smartlist_t *all_entry_guards = get_entry_guards(); - const node_t *test_node = NULL; - const entry_guard_t *test_entry = NULL; - const char *msg; - int which_node; - - (void) arg; + upgrade_circuits_data_t *data = arg; + + /* If any primary guards might be up, we can't upgrade any waiting + * circuits. + */ + mark_primary_guards_maybe_reachable(data->gs); + + smartlist_t *result = smartlist_new(); + int r; + setup_capture_of_logs(LOG_DEBUG); + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(result), OP_EQ, 0); + expect_log_msg_containing("not all primary guards were definitely down."); - /* The global entry guards smartlist should be empty now. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, 0); + done: + teardown_capture_of_logs(); + smartlist_free(result); +} - /* Walk the nodelist and add all nodes as entry guards. */ - our_nodelist = nodelist_get_list(); - tt_int_op(smartlist_len(our_nodelist), OP_EQ, HELPER_NUMBER_OF_DESCRIPTORS); +static void +test_entry_guard_upgrade_blocked_by_lack_of_waiting_circuits(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* If no circuits are waiting, we can't upgrade anything. (The test + * setup in this case was told not to make any of the circuits "waiting".) + */ + smartlist_t *result = smartlist_new(); + int r; + setup_capture_of_logs(LOG_DEBUG); + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(result), OP_EQ, 0); + expect_log_msg_containing("Considered upgrading guard-stalled circuits, " + "but didn't find any."); - SMARTLIST_FOREACH_BEGIN(our_nodelist, const node_t *, node) { - const node_t *node_tmp; - node_tmp = add_an_entry_guard(node, 0, 1, 0, 0); - tt_assert(node_tmp); + done: + teardown_capture_of_logs(); + smartlist_free(result); +} - tt_int_op(node->is_stable, OP_EQ, 0); - tt_int_op(node->is_fast, OP_EQ, 0); - } SMARTLIST_FOREACH_END(node); +static void +test_entry_guard_upgrade_blocked_by_better_circ_complete(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* We'll run through the logic of upgrade_a_circuit below... + * and then try again to make sure that circ2 isn't also upgraded. + */ + + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ1); + tt_ptr_op(data->guard1_state, OP_NE, NULL); + tt_int_op(data->guard1_state->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + + /* Now, try again. Make sure that circ2 isn't upgraded. */ + smartlist_clear(result); + setup_capture_of_logs(LOG_DEBUG); + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(result), OP_EQ, 0); + expect_log_msg_containing("At least one complete circuit had higher " + "priority, so not upgrading."); - /* Make sure the nodes were added as entry guards. */ - tt_int_op(smartlist_len(all_entry_guards), OP_EQ, - HELPER_NUMBER_OF_DESCRIPTORS); + done: + teardown_capture_of_logs(); + smartlist_free(result); +} - /* Now get a random test entry that we will use for this unit test. */ - which_node = 3; /* (chosen by fair dice roll) */ - test_entry = smartlist_get(all_entry_guards, which_node); +static void +test_entry_guard_upgrade_not_blocked_by_restricted_circ_complete(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* Once more, let circ1 become complete. But this time, we'll claim + * that circ2 was restricted to not use the same guard as circ1. */ + data->guard2_state->restrictions = + tor_malloc_zero(sizeof(entry_guard_restriction_t)); + memcpy(data->guard2_state->restrictions->exclude_id, + data->guard1->identity, DIGEST_LEN); + + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ1); + tt_ptr_op(data->guard1_state, OP_NE, NULL); + tt_int_op(data->guard1_state->state, OP_EQ, GUARD_CIRC_STATE_COMPLETE); + + /* Now, we try again. Since circ2 has a restriction that circ1 doesn't obey, + * circ2 _is_ eligible for upgrade. */ + smartlist_clear(result); + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc2 = smartlist_get(result, 0); + tt_ptr_op(oc2, OP_EQ, data->circ2); - /* Let's do some entry_is_live() tests! */ + done: + smartlist_free(result); +} - /* Require the node to be stable, but it's not. Should fail. - Also enable 'assume_reachable' because why not. */ - test_node = entry_is_live(test_entry, - ENTRY_NEED_UPTIME | ENTRY_ASSUME_REACHABLE, - &msg); - tt_assert(!test_node); +static void +test_entry_guard_upgrade_not_blocked_by_worse_circ_complete(void *arg) +{ + upgrade_circuits_data_t *data = arg; + smartlist_t *result = smartlist_new(); + /* here we manually make circ2 COMPLETE, and make sure that circ1 + * gets made complete anyway, since guard1 has higher priority + */ + update_approx_time(data->start + 300); + data->guard2_state->state = GUARD_CIRC_STATE_COMPLETE; + data->guard2_state->state_set_at = approx_time(); + update_approx_time(data->start + 301); + + /* Now, try again. Make sure that circ1 is approved. */ + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ1); - /* Require the node to be fast, but it's not. Should fail. */ - test_node = entry_is_live(test_entry, - ENTRY_NEED_CAPACITY | ENTRY_ASSUME_REACHABLE, - &msg); - tt_assert(!test_node); + done: + smartlist_free(result); +} - /* Don't impose any restrictions on the node. Should succeed. */ - test_node = entry_is_live(test_entry, 0, &msg); - tt_assert(test_node); - tt_ptr_op(test_node, OP_EQ, node_get_by_id(test_entry->identity)); +static void +test_entry_guard_upgrade_blocked_by_better_circ_pending(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* circ2 is done, but circ1 is still pending. Since circ1 is better, + * we won't upgrade circ2. */ + + /* XXXX Prop271 -- this is a kludge. I'm making sure circ1 _is_ better, + * by messing with the guards' confirmed_idx */ + make_guard_confirmed(data->gs, data->guard1); + { + int tmp; + tmp = data->guard1->confirmed_idx; + data->guard1->confirmed_idx = data->guard2->confirmed_idx; + data->guard2->confirmed_idx = tmp; + } - /* Require descriptor for this node. It has one so it should succeed. */ - test_node = entry_is_live(test_entry, ENTRY_NEED_DESCRIPTOR, &msg); - tt_assert(test_node); - tt_ptr_op(test_node, OP_EQ, node_get_by_id(test_entry->identity)); + smartlist_t *result = smartlist_new(); + setup_capture_of_logs(LOG_DEBUG); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 0); + tt_int_op(smartlist_len(result), OP_EQ, 0); + expect_log_msg_containing("but 1 pending circuit(s) had higher guard " + "priority, so not upgrading."); done: - ; /* XXX */ + teardown_capture_of_logs(); + smartlist_free(result); } -#define TEST_IPV4_ADDR "123.45.67.89" -#define TEST_IPV6_ADDR "[1234:5678:90ab:cdef::]" - static void -test_node_preferred_orport(void *arg) +test_entry_guard_upgrade_not_blocked_by_restricted_circ_pending(void *arg) { - (void)arg; - tor_addr_t ipv4_addr; - const uint16_t ipv4_port = 4444; - tor_addr_t ipv6_addr; - const uint16_t ipv6_port = 6666; - routerinfo_t node_ri; - node_t node; - tor_addr_port_t ap; + upgrade_circuits_data_t *data = arg; + /* circ2 is done, but circ1 is still pending. But when there is a + restriction on circ2 that circ1 can't satisfy, circ1 can't block + circ2. */ + + /* XXXX Prop271 -- this is a kludge. I'm making sure circ1 _is_ better, + * by messing with the guards' confirmed_idx */ + make_guard_confirmed(data->gs, data->guard1); + { + int tmp; + tmp = data->guard1->confirmed_idx; + data->guard1->confirmed_idx = data->guard2->confirmed_idx; + data->guard2->confirmed_idx = tmp; + } - /* Setup options */ - memset(&mocked_options, 0, sizeof(mocked_options)); - /* We don't test ClientPreferIPv6ORPort here, because it's used in - * nodelist_set_consensus to setup node.ipv6_preferred, which we set - * directly. */ - MOCK(get_options, mock_get_options); + data->guard2_state->restrictions = + tor_malloc_zero(sizeof(entry_guard_restriction_t)); + memcpy(data->guard2_state->restrictions->exclude_id, + data->guard1->identity, DIGEST_LEN); + + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ2); - /* Setup IP addresses */ - tor_addr_parse(&ipv4_addr, TEST_IPV4_ADDR); - tor_addr_parse(&ipv6_addr, TEST_IPV6_ADDR); + done: + smartlist_free(result); +} - /* Setup node_ri */ - memset(&node_ri, 0, sizeof(node_ri)); - node_ri.addr = tor_addr_to_ipv4h(&ipv4_addr); - node_ri.or_port = ipv4_port; - tor_addr_copy(&node_ri.ipv6_addr, &ipv6_addr); - node_ri.ipv6_orport = ipv6_port; +static void +test_entry_guard_upgrade_not_blocked_by_worse_circ_pending(void *arg) +{ + upgrade_circuits_data_t *data = arg; + + /* circ1 is done, but circ2 is still pending. Since circ1 is better, + * we will upgrade it. */ + smartlist_t *result = smartlist_new(); + int r; + r = entry_guards_upgrade_waiting_circuits(data->gs, + data->all_origin_circuits, + result); + tt_int_op(r, OP_EQ, 1); + tt_int_op(smartlist_len(result), OP_EQ, 1); + origin_circuit_t *oc = smartlist_get(result, 0); + tt_ptr_op(oc, OP_EQ, data->circ1); - /* Setup node */ - memset(&node, 0, sizeof(node)); - node.ri = &node_ri; + done: + smartlist_free(result); +} - /* Check the preferred address is IPv4 if we're only using IPv4, regardless - * of whether we prefer it or not */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 0; - node.ipv6_preferred = 0; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); - tt_assert(ap.port == ipv4_port); +static void +test_enty_guard_should_expire_waiting(void *arg) +{ + (void)arg; + circuit_guard_state_t *fake_state = tor_malloc_zero(sizeof(*fake_state)); + /* We'll leave "guard" unset -- it won't matter here. */ - node.ipv6_preferred = 1; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); - tt_assert(ap.port == ipv4_port); + /* No state? Can't expire. */ + tt_assert(! entry_guard_state_should_expire(NULL)); - /* Check the preferred address is IPv4 if we're using IPv4 and IPv6, but - * don't prefer the IPv6 address */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 1; - node.ipv6_preferred = 0; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv4_addr)); - tt_assert(ap.port == ipv4_port); + /* Let's try one that expires. */ + fake_state->state = GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD; + fake_state->state_set_at = + approx_time() - DFLT_NONPRIMARY_GUARD_IDLE_TIMEOUT - 1; - /* Check the preferred address is IPv6 if we prefer it and - * ClientUseIPv6 is 1, regardless of ClientUseIPv4 */ - mocked_options.ClientUseIPv4 = 1; - mocked_options.ClientUseIPv6 = 1; - node.ipv6_preferred = 1; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); - tt_assert(ap.port == ipv6_port); + tt_assert(entry_guard_state_should_expire(fake_state)); - mocked_options.ClientUseIPv4 = 0; - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); - tt_assert(ap.port == ipv6_port); + /* But it wouldn't expire if we changed the state. */ + fake_state->state = GUARD_CIRC_STATE_USABLE_IF_NO_BETTER_GUARD; + tt_assert(! entry_guard_state_should_expire(fake_state)); - /* Check the preferred address is IPv6 if we don't prefer it, but - * ClientUseIPv4 is 0 */ - mocked_options.ClientUseIPv4 = 0; - mocked_options.ClientUseIPv6 = 1; - node.ipv6_preferred = fascist_firewall_prefer_ipv6_orport(&mocked_options); - node_get_pref_orport(&node, &ap); - tt_assert(tor_addr_eq(&ap.addr, &ipv6_addr)); - tt_assert(ap.port == ipv6_port); + /* And it wouldn't have expired a few seconds ago. */ + fake_state->state = GUARD_CIRC_STATE_WAITING_FOR_BETTER_GUARD; + fake_state->state_set_at = + approx_time() - DFLT_NONPRIMARY_GUARD_IDLE_TIMEOUT + 5; + tt_assert(! entry_guard_state_should_expire(fake_state)); done: - UNMOCK(get_options); + tor_free(fake_state); } -static const struct testcase_setup_t fake_network = { - fake_network_setup, fake_network_cleanup +static const struct testcase_setup_t big_fake_network = { + big_fake_network_setup, big_fake_network_cleanup +}; + +static const struct testcase_setup_t upgrade_circuits = { + upgrade_circuits_setup, upgrade_circuits_cleanup }; +#define BFN_TEST(name) \ + { #name, test_entry_guard_ ## name, TT_FORK, &big_fake_network, NULL } + +#define UPGRADE_TEST(name, arg) \ + { #name, test_entry_guard_ ## name, TT_FORK, &upgrade_circuits, \ + (void*)(arg) } + struct testcase_t entrynodes_tests[] = { - { "entry_is_time_to_retry", test_entry_is_time_to_retry, - TT_FORK, NULL, NULL }, - { "choose_random_entry_no_guards", test_choose_random_entry_no_guards, - TT_FORK, &fake_network, NULL }, - { "choose_random_entry_one_possibleguard", - test_choose_random_entry_one_possible_guard, - TT_FORK, &fake_network, NULL }, - { "populate_live_entry_guards_1guard", - test_populate_live_entry_guards_1guard, - TT_FORK, &fake_network, NULL }, - { "populate_live_entry_guards_3guards", - test_populate_live_entry_guards_3guards, - TT_FORK, &fake_network, NULL }, - { "entry_guards_parse_state_simple", - test_entry_guards_parse_state_simple, - TT_FORK, &fake_network, NULL }, - { "entry_guards_parse_state_pathbias", - test_entry_guards_parse_state_pathbias, - TT_FORK, &fake_network, NULL }, - { "entry_guards_set_from_config", - test_entry_guards_set_from_config, - TT_FORK, &fake_network, NULL }, - { "entry_is_live", - test_entry_is_live, - TT_FORK, &fake_network, NULL }, { "node_preferred_orport", test_node_preferred_orport, 0, NULL, NULL }, + { "entry_guard_describe", test_entry_guard_describe, 0, NULL, NULL }, + { "randomize_time", test_entry_guard_randomize_time, 0, NULL, NULL }, + { "encode_for_state_minimal", + test_entry_guard_encode_for_state_minimal, 0, NULL, NULL }, + { "encode_for_state_maximal", + test_entry_guard_encode_for_state_maximal, 0, NULL, NULL }, + { "parse_from_state_minimal", + test_entry_guard_parse_from_state_minimal, 0, NULL, NULL }, + { "parse_from_state_maximal", + test_entry_guard_parse_from_state_maximal, 0, NULL, NULL }, + { "parse_from_state_failure", + test_entry_guard_parse_from_state_failure, 0, NULL, NULL }, + { "parse_from_state_partial_failure", + test_entry_guard_parse_from_state_partial_failure, 0, NULL, NULL }, + { "parse_from_state_full", + test_entry_guard_parse_from_state_full, TT_FORK, NULL, NULL }, + { "parse_from_state_broken", + test_entry_guard_parse_from_state_broken, TT_FORK, NULL, NULL }, + { "get_guard_selection_by_name", + test_entry_guard_get_guard_selection_by_name, TT_FORK, NULL, NULL }, + BFN_TEST(choose_selection_initial), + BFN_TEST(add_single_guard), + BFN_TEST(node_filter), + BFN_TEST(expand_sample), + BFN_TEST(expand_sample_small_net), + BFN_TEST(update_from_consensus_status), + BFN_TEST(update_from_consensus_repair), + BFN_TEST(update_from_consensus_remove), + BFN_TEST(confirming_guards), + BFN_TEST(sample_reachable_filtered), + BFN_TEST(sample_reachable_filtered_empty), + BFN_TEST(retry_unreachable), + BFN_TEST(manage_primary), + { "guard_preferred", test_entry_guard_guard_preferred, TT_FORK, NULL, NULL }, + BFN_TEST(select_for_circuit_no_confirmed), + BFN_TEST(select_for_circuit_confirmed), + BFN_TEST(select_for_circuit_highlevel_primary), + BFN_TEST(select_for_circuit_highlevel_confirm_other), + BFN_TEST(select_for_circuit_highlevel_primary_retry), + BFN_TEST(select_and_cancel), + BFN_TEST(drop_guards), + + UPGRADE_TEST(upgrade_a_circuit, "c1-done c2-done"), + UPGRADE_TEST(upgrade_blocked_by_live_primary_guards, "c1-done c2-done"), + UPGRADE_TEST(upgrade_blocked_by_lack_of_waiting_circuits, ""), + UPGRADE_TEST(upgrade_blocked_by_better_circ_complete, "c1-done c2-done"), + UPGRADE_TEST(upgrade_not_blocked_by_restricted_circ_complete, + "c1-done c2-done"), + UPGRADE_TEST(upgrade_not_blocked_by_worse_circ_complete, "c1-done c2-done"), + UPGRADE_TEST(upgrade_blocked_by_better_circ_pending, "c2-done"), + UPGRADE_TEST(upgrade_not_blocked_by_restricted_circ_pending, + "c2-done"), + UPGRADE_TEST(upgrade_not_blocked_by_worse_circ_pending, "c1-done"), + { "should_expire_waiting", test_enty_guard_should_expire_waiting, TT_FORK, + NULL, NULL }, + END_OF_TESTCASES }; diff --git a/src/test/test_helpers.c b/src/test/test_helpers.c index ae9fc7a243..5b84366e6d 100644 --- a/src/test/test_helpers.c +++ b/src/test/test_helpers.c @@ -10,8 +10,10 @@ #include "orconfig.h" #include "or.h" +#include "relay.h" #include "routerlist.h" #include "nodelist.h" +#include "buffers.h" #include "test.h" #include "test_helpers.h" @@ -22,6 +24,8 @@ DISABLE_GCC_WARNING(overlength-strings) * at large. */ #endif #include "test_descriptors.inc" +#include "or.h" +#include "circuitlist.h" #ifdef HAVE_CFLAG_WOVERLENGTH_STRINGS ENABLE_GCC_WARNING(overlength-strings) #endif @@ -92,3 +96,50 @@ helper_setup_fake_routerlist(void) UNMOCK(router_descriptor_is_older_than); } +void +connection_write_to_buf_mock(const char *string, size_t len, + connection_t *conn, int zlib) +{ + (void) zlib; + + tor_assert(string); + tor_assert(conn); + + write_to_buf(string, len, conn->outbuf); +} + +/* Set up a fake origin circuit with the specified number of cells, + * Return a pointer to the newly-created dummy circuit */ +circuit_t * +dummy_origin_circuit_new(int n_cells) +{ + origin_circuit_t *circ = origin_circuit_new(); + int i; + cell_t cell; + + for (i=0; i < n_cells; ++i) { + crypto_rand((void*)&cell, sizeof(cell)); + cell_queue_append_packed_copy(TO_CIRCUIT(circ), + &TO_CIRCUIT(circ)->n_chan_cells, + 1, &cell, 1, 0); + } + + TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; + return TO_CIRCUIT(circ); +} + +/** Mock-replacement. As tor_addr_lookup, but always fails on any + * address containing a !. This is necessary for running the unit tests + * on networks where DNS hijackers think it's helpful to give answers + * for things like 1.2.3.4.5 or "invalidstuff!!" + */ +int +mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, + uint16_t family, tor_addr_t *out) +{ + if (name && strchr(name, '!')) { + return -1; + } + return tor_addr_lookup__real(name, family, out); +} + diff --git a/src/test/test_helpers.h b/src/test/test_helpers.h index 684375e1b1..c6d4d9c41f 100644 --- a/src/test/test_helpers.h +++ b/src/test/test_helpers.h @@ -6,11 +6,20 @@ const char *get_yesterday_date_str(void); +circuit_t * dummy_origin_circuit_new(int num_cells); + /* Number of descriptors contained in test_descriptors.txt. */ #define HELPER_NUMBER_OF_DESCRIPTORS 8 void helper_setup_fake_routerlist(void); +#define GET(path) "GET " path " HTTP/1.0\r\n\r\n" +void connection_write_to_buf_mock(const char *string, size_t len, + connection_t *conn, int zlib); + +int mock_tor_addr_lookup__fail_on_bad_addrs(const char *name, + uint16_t family, tor_addr_t *out); + extern const char TEST_DESCRIPTORS[]; #endif diff --git a/src/test/test_hs.c b/src/test/test_hs.c index 8237bbc50e..fbaabe91d8 100644 --- a/src/test/test_hs.c +++ b/src/test/test_hs.c @@ -14,6 +14,7 @@ #include "test.h" #include "control.h" #include "config.h" +#include "hs_common.h" #include "rendcommon.h" #include "rendservice.h" #include "routerset.h" @@ -136,7 +137,7 @@ test_hs_desc_event(void *arg) #define STR_DESC_ID_BASE32 "hba3gmcgpfivzfhx5rtfqkfdhv65yrj3" int ret; - rend_data_t rend_query; + rend_data_v2_t rend_query; const char *expected_msg; char desc_id_base32[REND_DESC_ID_V2_LEN_BASE32 + 1]; @@ -148,12 +149,13 @@ test_hs_desc_event(void *arg) /* setup rend_query struct */ memset(&rend_query, 0, sizeof(rend_query)); + rend_query.base_.version = 2; strncpy(rend_query.onion_address, STR_HS_ADDR, REND_SERVICE_ID_LEN_BASE32+1); rend_query.auth_type = REND_NO_AUTH; - rend_query.hsdirs_fp = smartlist_new(); - smartlist_add(rend_query.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID, - DIGEST_LEN)); + rend_query.base_.hsdirs_fp = smartlist_new(); + smartlist_add(rend_query.base_.hsdirs_fp, tor_memdup(HSDIR_EXIST_ID, + DIGEST_LEN)); /* Compute descriptor ID for replica 0, should be STR_DESC_ID_BASE32. */ ret = rend_compute_v2_desc_id(rend_query.descriptor_id[0], @@ -167,7 +169,7 @@ test_hs_desc_event(void *arg) sizeof(desc_id_base32)); /* test request event */ - control_event_hs_descriptor_requested(&rend_query, HSDIR_EXIST_ID, + control_event_hs_descriptor_requested(&rend_query.base_, HSDIR_EXIST_ID, STR_DESC_ID_BASE32); expected_msg = "650 HS_DESC REQUESTED "STR_HS_ADDR" NO_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32 "\r\n"; @@ -178,7 +180,7 @@ test_hs_desc_event(void *arg) /* test received event */ rend_query.auth_type = REND_BASIC_AUTH; control_event_hs_descriptor_received(rend_query.onion_address, - &rend_query, HSDIR_EXIST_ID); + &rend_query.base_, HSDIR_EXIST_ID); expected_msg = "650 HS_DESC RECEIVED "STR_HS_ADDR" BASIC_AUTH "\ STR_HSDIR_EXIST_LONGNAME " " STR_DESC_ID_BASE32"\r\n"; tt_assert(received_msg); @@ -187,7 +189,7 @@ test_hs_desc_event(void *arg) /* test failed event */ rend_query.auth_type = REND_STEALTH_AUTH; - control_event_hs_descriptor_failed(&rend_query, + control_event_hs_descriptor_failed(&rend_query.base_, HSDIR_NONE_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" STEALTH_AUTH "\ @@ -198,7 +200,7 @@ test_hs_desc_event(void *arg) /* test invalid auth type */ rend_query.auth_type = 999; - control_event_hs_descriptor_failed(&rend_query, + control_event_hs_descriptor_failed(&rend_query.base_, HSDIR_EXIST_ID, "QUERY_REJECTED"); expected_msg = "650 HS_DESC FAILED "STR_HS_ADDR" UNKNOWN "\ @@ -221,8 +223,8 @@ test_hs_desc_event(void *arg) tt_str_op(received_msg, OP_EQ, exp_msg); tor_free(received_msg); tor_free(exp_msg); - SMARTLIST_FOREACH(rend_query.hsdirs_fp, char *, d, tor_free(d)); - smartlist_free(rend_query.hsdirs_fp); + SMARTLIST_FOREACH(rend_query.base_.hsdirs_fp, char *, d, tor_free(d)); + smartlist_free(rend_query.base_.hsdirs_fp); done: UNMOCK(queue_control_event_string); @@ -322,42 +324,46 @@ test_hs_rend_data(void *arg) client = rend_data_client_create(STR_HS_ADDR, desc_id, client_cookie, REND_NO_AUTH); tt_assert(client); - tt_int_op(client->auth_type, ==, REND_NO_AUTH); - tt_str_op(client->onion_address, OP_EQ, STR_HS_ADDR); - tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); - tt_mem_op(client->descriptor_cookie, OP_EQ, client_cookie, + rend_data_v2_t *client_v2 = TO_REND_DATA_V2(client); + tt_int_op(client_v2->auth_type, ==, REND_NO_AUTH); + tt_str_op(client_v2->onion_address, OP_EQ, STR_HS_ADDR); + tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); + tt_mem_op(client_v2->descriptor_cookie, OP_EQ, client_cookie, sizeof(client_cookie)); tt_assert(client->hsdirs_fp); tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - int ret = rend_compute_v2_desc_id(desc_id, client->onion_address, - client->descriptor_cookie, now, rep); + int ret = rend_compute_v2_desc_id(desc_id, client_v2->onion_address, + client_v2->descriptor_cookie, now, rep); /* That shouldn't never fail. */ tt_int_op(ret, ==, 0); - tt_mem_op(client->descriptor_id[rep], OP_EQ, desc_id, sizeof(desc_id)); + tt_mem_op(client_v2->descriptor_id[rep], OP_EQ, desc_id, + sizeof(desc_id)); } /* The rest should be zeroed because this is a client request. */ - tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1); /* Test dup(). */ client_dup = rend_data_dup(client); tt_assert(client_dup); - tt_int_op(client_dup->auth_type, ==, client->auth_type); - tt_str_op(client_dup->onion_address, OP_EQ, client->onion_address); - tt_mem_op(client_dup->desc_id_fetch, OP_EQ, client->desc_id_fetch, - sizeof(client_dup->desc_id_fetch)); - tt_mem_op(client_dup->descriptor_cookie, OP_EQ, client->descriptor_cookie, - sizeof(client_dup->descriptor_cookie)); + rend_data_v2_t *client_dup_v2 = TO_REND_DATA_V2(client_dup); + tt_int_op(client_dup_v2->auth_type, ==, client_v2->auth_type); + tt_str_op(client_dup_v2->onion_address, OP_EQ, client_v2->onion_address); + tt_mem_op(client_dup_v2->desc_id_fetch, OP_EQ, client_v2->desc_id_fetch, + sizeof(client_dup_v2->desc_id_fetch)); + tt_mem_op(client_dup_v2->descriptor_cookie, OP_EQ, + client_v2->descriptor_cookie, + sizeof(client_dup_v2->descriptor_cookie)); tt_assert(client_dup->hsdirs_fp); tt_int_op(smartlist_len(client_dup->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - tt_mem_op(client_dup->descriptor_id[rep], OP_EQ, - client->descriptor_id[rep], DIGEST_LEN); + tt_mem_op(client_dup_v2->descriptor_id[rep], OP_EQ, + client_v2->descriptor_id[rep], DIGEST_LEN); } /* The rest should be zeroed because this is a client request. */ - tt_int_op(tor_digest_is_zero(client_dup->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client_dup_v2->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client_dup->rend_cookie), ==, 1); rend_data_free(client); client = NULL; @@ -373,18 +379,19 @@ test_hs_rend_data(void *arg) * zeroed out. */ client = rend_data_client_create(NULL, desc_id, NULL, REND_BASIC_AUTH); tt_assert(client); - tt_int_op(client->auth_type, ==, REND_BASIC_AUTH); - tt_int_op(strlen(client->onion_address), ==, 0); - tt_mem_op(client->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); - tt_int_op(tor_mem_is_zero(client->descriptor_cookie, - sizeof(client->descriptor_cookie)), ==, 1); + client_v2 = TO_REND_DATA_V2(client); + tt_int_op(client_v2->auth_type, ==, REND_BASIC_AUTH); + tt_int_op(strlen(client_v2->onion_address), ==, 0); + tt_mem_op(client_v2->desc_id_fetch, OP_EQ, desc_id, sizeof(desc_id)); + tt_int_op(tor_mem_is_zero(client_v2->descriptor_cookie, + sizeof(client_v2->descriptor_cookie)), ==, 1); tt_assert(client->hsdirs_fp); tt_int_op(smartlist_len(client->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - tt_int_op(tor_digest_is_zero(client->descriptor_id[rep]), ==, 1); + tt_int_op(tor_digest_is_zero(client_v2->descriptor_id[rep]), ==, 1); } /* The rest should be zeroed because this is a client request. */ - tt_int_op(tor_digest_is_zero(client->rend_pk_digest), ==, 1); + tt_int_op(tor_digest_is_zero(client_v2->rend_pk_digest), ==, 1); tt_int_op(tor_digest_is_zero(client->rend_cookie), ==, 1); rend_data_free(client); client = NULL; @@ -398,37 +405,39 @@ test_hs_rend_data(void *arg) service = rend_data_service_create(STR_HS_ADDR, rend_pk_digest, rend_cookie, REND_NO_AUTH); tt_assert(service); - tt_int_op(service->auth_type, ==, REND_NO_AUTH); - tt_str_op(service->onion_address, OP_EQ, STR_HS_ADDR); - tt_mem_op(service->rend_pk_digest, OP_EQ, rend_pk_digest, + rend_data_v2_t *service_v2 = TO_REND_DATA_V2(service); + tt_int_op(service_v2->auth_type, ==, REND_NO_AUTH); + tt_str_op(service_v2->onion_address, OP_EQ, STR_HS_ADDR); + tt_mem_op(service_v2->rend_pk_digest, OP_EQ, rend_pk_digest, sizeof(rend_pk_digest)); tt_mem_op(service->rend_cookie, OP_EQ, rend_cookie, sizeof(rend_cookie)); tt_assert(service->hsdirs_fp); tt_int_op(smartlist_len(service->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - tt_int_op(tor_digest_is_zero(service->descriptor_id[rep]), ==, 1); + tt_int_op(tor_digest_is_zero(service_v2->descriptor_id[rep]), ==, 1); } /* The rest should be zeroed because this is a service request. */ - tt_int_op(tor_digest_is_zero(service->descriptor_cookie), ==, 1); - tt_int_op(tor_digest_is_zero(service->desc_id_fetch), ==, 1); + tt_int_op(tor_digest_is_zero(service_v2->descriptor_cookie), ==, 1); + tt_int_op(tor_digest_is_zero(service_v2->desc_id_fetch), ==, 1); /* Test dup(). */ service_dup = rend_data_dup(service); + rend_data_v2_t *service_dup_v2 = TO_REND_DATA_V2(service_dup); tt_assert(service_dup); - tt_int_op(service_dup->auth_type, ==, service->auth_type); - tt_str_op(service_dup->onion_address, OP_EQ, service->onion_address); - tt_mem_op(service_dup->rend_pk_digest, OP_EQ, service->rend_pk_digest, - sizeof(service_dup->rend_pk_digest)); + tt_int_op(service_dup_v2->auth_type, ==, service_v2->auth_type); + tt_str_op(service_dup_v2->onion_address, OP_EQ, service_v2->onion_address); + tt_mem_op(service_dup_v2->rend_pk_digest, OP_EQ, service_v2->rend_pk_digest, + sizeof(service_dup_v2->rend_pk_digest)); tt_mem_op(service_dup->rend_cookie, OP_EQ, service->rend_cookie, sizeof(service_dup->rend_cookie)); tt_assert(service_dup->hsdirs_fp); tt_int_op(smartlist_len(service_dup->hsdirs_fp), ==, 0); for (rep = 0; rep < REND_NUMBER_OF_NON_CONSECUTIVE_REPLICAS; rep++) { - tt_int_op(tor_digest_is_zero(service_dup->descriptor_id[rep]), ==, 1); + tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_id[rep]), ==, 1); } /* The rest should be zeroed because this is a service request. */ - tt_int_op(tor_digest_is_zero(service_dup->descriptor_cookie), ==, 1); - tt_int_op(tor_digest_is_zero(service_dup->desc_id_fetch), ==, 1); + tt_int_op(tor_digest_is_zero(service_dup_v2->descriptor_cookie), ==, 1); + tt_int_op(tor_digest_is_zero(service_dup_v2->desc_id_fetch), ==, 1); done: rend_data_free(service); @@ -778,6 +787,126 @@ test_single_onion_poisoning(void *arg) tor_free(mock_options->DataDirectory); } +static rend_service_t * +helper_create_rend_service(const char *path) +{ + rend_service_t *s = tor_malloc_zero(sizeof(rend_service_t)); + s->ports = smartlist_new(); + s->intro_nodes = smartlist_new(); + s->expiring_nodes = smartlist_new(); + if (path) { + s->directory = tor_strdup(path); + } + return s; +} + +static void +test_prune_services_on_reload(void *arg) +{ + smartlist_t *new = smartlist_new(), *old = smartlist_new(); + /* Non ephemeral service. */ + rend_service_t *s1 = helper_create_rend_service("SomePath"); + /* Create a non ephemeral service with the _same_ path as so we can test the + * transfer of introduction point between the same services on reload. */ + rend_service_t *s2 = helper_create_rend_service(s1->directory); + /* Ephemeral service (directory is NULL). */ + rend_service_t *e1 = helper_create_rend_service(NULL); + rend_service_t *e2 = helper_create_rend_service(NULL); + + (void) arg; + + { + /* Add both services to the old list. */ + smartlist_add(old, s1); + smartlist_add(old, e1); + /* Only put the non ephemeral in the new list. */ + smartlist_add(new, s1); + prune_services_on_reload(old, new); + /* We expect that the ephemeral one is in the new list but removed from + * the old one. */ + tt_int_op(smartlist_len(old), OP_EQ, 1); + tt_assert(smartlist_get(old, 0) == s1); + tt_int_op(smartlist_len(new), OP_EQ, 2); + tt_assert(smartlist_get(new, 0) == s1); + tt_assert(smartlist_get(new, 1) == e1); + /* Cleanup for next test. */ + smartlist_clear(new); + smartlist_clear(old); + } + + { + /* This test will make sure that only the ephemeral service is kept if the + * new list is empty. The old list should contain only the non ephemeral + * one. */ + smartlist_add(old, s1); + smartlist_add(old, e1); + prune_services_on_reload(old, new); + tt_int_op(smartlist_len(old), OP_EQ, 1); + tt_assert(smartlist_get(old, 0) == s1); + tt_int_op(smartlist_len(new), OP_EQ, 1); + tt_assert(smartlist_get(new, 0) == e1); + /* Cleanup for next test. */ + smartlist_clear(new); + smartlist_clear(old); + } + + { + /* This test makes sure that the new list stays the same even from the old + * list being completely different. */ + smartlist_add(new, s1); + smartlist_add(new, e1); + prune_services_on_reload(old, new); + tt_int_op(smartlist_len(old), OP_EQ, 0); + tt_int_op(smartlist_len(new), OP_EQ, 2); + tt_assert(smartlist_get(new, 0) == s1); + tt_assert(smartlist_get(new, 1) == e1); + /* Cleanup for next test. */ + smartlist_clear(new); + } + + { + rend_intro_point_t ip1; + /* This IP should be found in the s2 service after pruning. */ + smartlist_add(s1->intro_nodes, &ip1); + /* Setup our list. */ + smartlist_add(old, s1); + smartlist_add(new, s2); + prune_services_on_reload(old, new); + tt_int_op(smartlist_len(old), OP_EQ, 1); + /* Intro nodes have been moved to the s2 in theory so it must be empty. */ + tt_int_op(smartlist_len(s1->intro_nodes), OP_EQ, 0); + tt_int_op(smartlist_len(new), OP_EQ, 1); + rend_service_t *elem = smartlist_get(new, 0); + tt_assert(elem); + tt_assert(elem == s2); + tt_int_op(smartlist_len(elem->intro_nodes), OP_EQ, 1); + tt_assert(smartlist_get(elem->intro_nodes, 0) == &ip1); + smartlist_clear(s1->intro_nodes); + smartlist_clear(s2->intro_nodes); + /* Cleanup for next test. */ + smartlist_clear(new); + smartlist_clear(old); + } + + { + /* Test two ephemeral services. */ + smartlist_add(old, e1); + smartlist_add(old, e2); + prune_services_on_reload(old, new); + /* Check if they've all been transfered. */ + tt_int_op(smartlist_len(old), OP_EQ, 0); + tt_int_op(smartlist_len(new), OP_EQ, 2); + } + + done: + rend_service_free(s1); + rend_service_free(s2); + rend_service_free(e1); + rend_service_free(e2); + smartlist_free(new); + smartlist_free(old); +} + struct testcase_t hs_tests[] = { { "hs_rend_data", test_hs_rend_data, TT_FORK, NULL, NULL }, @@ -798,6 +927,9 @@ struct testcase_t hs_tests[] = { TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR2) }, { "single_onion_poisoning_create_dir_both", test_single_onion_poisoning, TT_FORK, &passthrough_setup, (void*)(CREATE_HS_DIR1 | CREATE_HS_DIR2) }, + { "prune_services_on_reload", test_prune_services_on_reload, TT_FORK, + NULL, NULL }, + END_OF_TESTCASES }; diff --git a/src/test/test_hs_cache.c b/src/test/test_hs_cache.c new file mode 100644 index 0000000000..1943d0ffac --- /dev/null +++ b/src/test/test_hs_cache.c @@ -0,0 +1,528 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_cache.c + * \brief Test hidden service caches. + */ + +#define CONNECTION_PRIVATE +#define HS_CACHE_PRIVATE + +#include "ed25519_cert.h" +#include "hs_cache.h" +#include "rendcache.h" +#include "directory.h" +#include "connection.h" + +#include "test_helpers.h" +#include "test.h" + +/* Build an intro point using a blinded key and an address. */ +static hs_desc_intro_point_t * +helper_build_intro_point(const ed25519_keypair_t *blinded_kp, + const char *addr) +{ + int ret; + ed25519_keypair_t auth_kp; + hs_desc_intro_point_t *intro_point = NULL; + hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip)); + ip->link_specifiers = smartlist_new(); + + { + hs_desc_link_specifier_t *ls = tor_malloc_zero(sizeof(*ls)); + ls->u.ap.port = 9001; + int family = tor_addr_parse(&ls->u.ap.addr, addr); + switch (family) { + case AF_INET: + ls->type = LS_IPV4; + break; + case AF_INET6: + ls->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); + } + + ret = ed25519_keypair_generate(&auth_kp, 0); + tt_int_op(ret, ==, 0); + ip->auth_key_cert = tor_cert_create(blinded_kp, CERT_TYPE_AUTH_HS_IP_KEY, + &auth_kp.pubkey, time(NULL), + HS_DESC_CERT_LIFETIME, + CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(ip->auth_key_cert); + + ret = curve25519_keypair_generate(&ip->enc_key.curve25519, 0); + tt_int_op(ret, ==, 0); + ip->enc_key_type = HS_DESC_KEY_TYPE_CURVE25519; + intro_point = ip; + done: + return intro_point; +} + +/* Return a valid hs_descriptor_t object. */ +static hs_descriptor_t * +helper_build_hs_desc(uint64_t revision_counter, uint32_t lifetime, + ed25519_public_key_t *signing_pubkey) +{ + int ret; + ed25519_keypair_t blinded_kp; + hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc)); + + desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX; + + /* Copy only the public key into the descriptor. */ + memcpy(&desc->plaintext_data.signing_pubkey, signing_pubkey, + sizeof(ed25519_public_key_t)); + + ret = ed25519_keypair_generate(&blinded_kp, 0); + tt_int_op(ret, ==, 0); + /* Copy only the public key into the descriptor. */ + memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey, + sizeof(ed25519_public_key_t)); + + desc->plaintext_data.signing_key_cert = + tor_cert_create(&blinded_kp, CERT_TYPE_SIGNING_HS_DESC, signing_pubkey, + time(NULL), 3600, CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(desc->plaintext_data.signing_key_cert); + desc->plaintext_data.revision_counter = revision_counter; + desc->plaintext_data.lifetime_sec = lifetime; + + /* Setup encrypted data section. */ + desc->encrypted_data.create2_ntor = 1; + desc->encrypted_data.auth_types = smartlist_new(); + smartlist_add(desc->encrypted_data.auth_types, tor_strdup("ed25519")); + desc->encrypted_data.intro_points = smartlist_new(); + /* Add an intro point. */ + smartlist_add(desc->encrypted_data.intro_points, + helper_build_intro_point(&blinded_kp, "1.2.3.4")); + + descp = desc; + done: + return descp; +} + +/* Static variable used to encoded the HSDir query. */ +static char query_b64[256]; + +/* Build an HSDir query using a ed25519 public key. */ +static const char * +helper_get_hsdir_query(const hs_descriptor_t *desc) +{ + ed25519_public_to_base64(query_b64, &desc->plaintext_data.blinded_pubkey); + return query_b64; +} + +static void +init_test(void) +{ + /* Always needed. Initialize the subsystem. */ + hs_cache_init(); + /* We need the v2 cache since our OOM and cache cleanup does poke at it. */ + rend_cache_init(); +} + +static void +test_directory(void *arg) +{ + int ret; + size_t oom_size; + char *desc1_str = NULL; + const char *desc_out; + ed25519_keypair_t signing_kp1; + hs_descriptor_t *desc1 = NULL; + + (void) arg; + + init_test(); + /* Generate a valid descriptor with normal values. */ + ret = ed25519_keypair_generate(&signing_kp1, 0); + tt_int_op(ret, ==, 0); + desc1 = helper_build_hs_desc(42, 3 * 60 * 60, &signing_kp1.pubkey); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str); + tt_int_op(ret, OP_EQ, 0); + + /* Very first basic test, should be able to be stored, survive a + * clean, found with a lookup and then cleaned by our OOM. */ + { + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + /* Re-add, it should fail since we already have it. */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, -1); + /* Try to clean now which should be fine, there is at worst few seconds + * between the store and this call. */ + hs_cache_clean_as_dir(time(NULL)); + /* We should find it in our cache. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(desc_out, OP_EQ, desc1_str); + /* Tell our OOM to run and to at least remove a byte which will result in + * removing the descriptor from our cache. */ + oom_size = hs_cache_handle_oom(time(NULL), 1); + tt_int_op(oom_size, >=, 1); + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL); + tt_int_op(ret, OP_EQ, 0); + } + + /* Store two descriptors and remove the expiring one only. */ + { + ed25519_keypair_t signing_kp_zero; + ret = ed25519_keypair_generate(&signing_kp_zero, 0); + tt_int_op(ret, ==, 0); + hs_descriptor_t *desc_zero_lifetime; + desc_zero_lifetime = helper_build_hs_desc(1, 0, &signing_kp_zero.pubkey); + tt_assert(desc_zero_lifetime); + char *desc_zero_lifetime_str; + ret = hs_desc_encode_descriptor(desc_zero_lifetime, &signing_kp_zero, + &desc_zero_lifetime_str); + tt_int_op(ret, OP_EQ, 0); + + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + ret = hs_cache_store_as_dir(desc_zero_lifetime_str); + tt_int_op(ret, OP_EQ, 0); + /* This one should clear out our zero lifetime desc. */ + hs_cache_clean_as_dir(time(NULL)); + /* We should find desc1 in our cache. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(desc_out, OP_EQ, desc1_str); + /* We should NOT find our zero lifetime desc in our cache. */ + ret = hs_cache_lookup_as_dir(3, + helper_get_hsdir_query(desc_zero_lifetime), + NULL); + tt_int_op(ret, OP_EQ, 0); + /* Cleanup our entire cache. */ + oom_size = hs_cache_handle_oom(time(NULL), 1); + tt_int_op(oom_size, >=, 1); + hs_descriptor_free(desc_zero_lifetime); + tor_free(desc_zero_lifetime_str); + } + + /* Throw junk at it. */ + { + ret = hs_cache_store_as_dir("blah"); + tt_int_op(ret, OP_EQ, -1); + /* Poor attempt at tricking the decoding. */ + ret = hs_cache_store_as_dir("hs-descriptor 3\nJUNK"); + tt_int_op(ret, OP_EQ, -1); + /* Undecodable base64 query. */ + ret = hs_cache_lookup_as_dir(3, "blah", NULL); + tt_int_op(ret, OP_EQ, -1); + /* Decodable base64 query but wrong ed25519 size. */ + ret = hs_cache_lookup_as_dir(3, "dW5pY29ybg==", NULL); + tt_int_op(ret, OP_EQ, -1); + } + + /* Test descriptor replacement with revision counter. */ + { + char *new_desc_str; + + /* Add a descriptor. */ + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out); + tt_int_op(ret, OP_EQ, 1); + /* Bump revision counter. */ + desc1->plaintext_data.revision_counter++; + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &new_desc_str); + tt_int_op(ret, OP_EQ, 0); + ret = hs_cache_store_as_dir(new_desc_str); + tt_int_op(ret, OP_EQ, 0); + /* Look it up, it should have been replaced. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), &desc_out); + tt_int_op(ret, OP_EQ, 1); + tt_str_op(desc_out, OP_EQ, new_desc_str); + tor_free(new_desc_str); + } + + done: + hs_descriptor_free(desc1); + tor_free(desc1_str); +} + +static void +test_clean_as_dir(void *arg) +{ + size_t ret; + char *desc1_str = NULL; + time_t now = time(NULL); + hs_descriptor_t *desc1 = NULL; + ed25519_keypair_t signing_kp1; + + (void) arg; + + init_test(); + + /* Generate a valid descriptor with values. */ + ret = ed25519_keypair_generate(&signing_kp1, 0); + tt_int_op(ret, ==, 0); + desc1 = helper_build_hs_desc(42, 3 * 60 * 60, &signing_kp1.pubkey); + tt_assert(desc1); + ret = hs_desc_encode_descriptor(desc1, &signing_kp1, &desc1_str); + tt_int_op(ret, OP_EQ, 0); + ret = hs_cache_store_as_dir(desc1_str); + tt_int_op(ret, OP_EQ, 0); + + /* With the lifetime being 3 hours, a cleanup shouldn't remove it. */ + ret = cache_clean_v3_as_dir(now, 0); + tt_int_op(ret, ==, 0); + /* Should be present after clean up. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL); + tt_int_op(ret, OP_EQ, 1); + /* Set a cutoff 100 seconds in the past. It should not remove the entry + * since the entry is still recent enough. */ + ret = cache_clean_v3_as_dir(now, now - 100); + tt_int_op(ret, ==, 0); + /* Should be present after clean up. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL); + tt_int_op(ret, OP_EQ, 1); + /* Set a cutoff of 100 seconds in the future. It should remove the entry + * that we've just added since it's not too old for the cutoff. */ + ret = cache_clean_v3_as_dir(now, now + 100); + tt_int_op(ret, >, 0); + /* Shouldn't be present after clean up. */ + ret = hs_cache_lookup_as_dir(3, helper_get_hsdir_query(desc1), NULL); + tt_int_op(ret, OP_EQ, 0); + + done: + hs_descriptor_free(desc1); + tor_free(desc1_str); +} + +/* Test helper: Fetch an HS descriptor from an HSDir (for the hidden service + with <b>blinded_key</b>. Return the received descriptor string. */ +static char * +helper_fetch_desc_from_hsdir(const ed25519_public_key_t *blinded_key) +{ + int retval; + + char *received_desc = NULL; + char *hsdir_query_str = NULL; + + /* The dir conn we are going to simulate */ + dir_connection_t *conn = NULL; + + /* First extract the blinded public key that we are going to use in our + query, and then build the actual query string. */ + { + char hsdir_cache_key[ED25519_BASE64_LEN+1]; + + retval = ed25519_public_to_base64(hsdir_cache_key, + blinded_key); + tt_int_op(retval, ==, 0); + tor_asprintf(&hsdir_query_str, GET("/tor/hs/3/%s"), hsdir_cache_key); + } + + /* Simulate an HTTP GET request to the HSDir */ + conn = dir_connection_new(AF_INET); + tor_addr_from_ipv4h(&conn->base_.addr, 0x7f000001); + TO_CONN(conn)->linked = 1;/* Pretend the conn is encrypted :) */ + retval = directory_handle_command_get(conn, hsdir_query_str, + NULL, 0); + tt_int_op(retval, OP_EQ, 0); + + /* Read the descriptor that the HSDir just served us */ + { + char *headers = NULL; + size_t body_used = 0; + + fetch_from_buf_http(TO_CONN(conn)->outbuf, &headers, MAX_HEADERS_SIZE, + &received_desc, &body_used, 10000, 0); + tor_free(headers); + } + + done: + tor_free(hsdir_query_str); + if (conn) + connection_free_(TO_CONN(conn)); + + return received_desc; +} + +/* Publish a descriptor to the HSDir, then fetch it. Check that the received + descriptor matches the published one. */ +static void +test_upload_and_download_hs_desc(void *arg) +{ + int retval; + hs_descriptor_t *published_desc = NULL; + + char *published_desc_str = NULL; + char *received_desc_str = NULL; + + (void) arg; + + /* Initialize HSDir cache subsystem */ + init_test(); + + /* Test a descriptor not found in the directory cache. */ + { + ed25519_public_key_t blinded_key; + memset(&blinded_key.pubkey, 'A', sizeof(blinded_key.pubkey)); + received_desc_str = helper_fetch_desc_from_hsdir(&blinded_key); + tt_int_op(strlen(received_desc_str), OP_EQ, 0); + tor_free(received_desc_str); + } + + /* Generate a valid descriptor with normal values. */ + { + ed25519_keypair_t signing_kp; + retval = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(retval, ==, 0); + published_desc = helper_build_hs_desc(42, 3 * 60 * 60, &signing_kp.pubkey); + tt_assert(published_desc); + retval = hs_desc_encode_descriptor(published_desc, &signing_kp, + &published_desc_str); + tt_int_op(retval, OP_EQ, 0); + } + + /* Publish descriptor to the HSDir */ + { + retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str); + tt_int_op(retval, ==, 200); + } + + /* Simulate a fetch of the previously published descriptor */ + { + const ed25519_public_key_t *blinded_key; + blinded_key = &published_desc->plaintext_data.blinded_pubkey; + received_desc_str = helper_fetch_desc_from_hsdir(blinded_key); + } + + /* Verify we received the exact same descriptor we published earlier */ + tt_str_op(received_desc_str, OP_EQ, published_desc_str); + tor_free(received_desc_str); + + /* With a valid descriptor in the directory cache, try again an invalid. */ + { + ed25519_public_key_t blinded_key; + memset(&blinded_key.pubkey, 'A', sizeof(blinded_key.pubkey)); + received_desc_str = helper_fetch_desc_from_hsdir(&blinded_key); + tt_int_op(strlen(received_desc_str), OP_EQ, 0); + } + + done: + tor_free(received_desc_str); + tor_free(published_desc_str); + hs_descriptor_free(published_desc); +} + +/* Test that HSDirs reject outdated descriptors based on their revision + * counter. Also test that HSDirs correctly replace old descriptors with newer + * descriptors. */ +static void +test_hsdir_revision_counter_check(void *arg) +{ + int retval; + + ed25519_keypair_t signing_kp; + + hs_descriptor_t *published_desc = NULL; + char *published_desc_str = NULL; + + char *received_desc_str = NULL; + hs_descriptor_t *received_desc = NULL; + + (void) arg; + + /* Initialize HSDir cache subsystem */ + init_test(); + + /* Generate a valid descriptor with normal values. */ + { + retval = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(retval, ==, 0); + published_desc = helper_build_hs_desc(1312, 3 * 60 * 60, + &signing_kp.pubkey); + tt_assert(published_desc); + retval = hs_desc_encode_descriptor(published_desc, &signing_kp, + &published_desc_str); + tt_int_op(retval, OP_EQ, 0); + } + + /* Publish descriptor to the HSDir */ + { + retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str); + tt_int_op(retval, ==, 200); + } + + /* Try publishing again with the same revision counter: Should fail. */ + { + retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str); + tt_int_op(retval, ==, 400); + } + + /* Fetch the published descriptor and validate the revision counter. */ + { + const ed25519_public_key_t *blinded_key; + + blinded_key = &published_desc->plaintext_data.blinded_pubkey; + received_desc_str = helper_fetch_desc_from_hsdir(blinded_key); + + retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc); + tt_int_op(retval, ==, 0); + tt_assert(received_desc); + + /* Check that the revision counter is correct */ + tt_u64_op(received_desc->plaintext_data.revision_counter, ==, 1312); + + hs_descriptor_free(received_desc); + received_desc = NULL; + tor_free(received_desc_str); + } + + /* Increment the revision counter and try again. Should work. */ + { + published_desc->plaintext_data.revision_counter = 1313; + tor_free(published_desc_str); + retval = hs_desc_encode_descriptor(published_desc, &signing_kp, + &published_desc_str); + tt_int_op(retval, OP_EQ, 0); + + retval = handle_post_hs_descriptor("/tor/hs/3/publish",published_desc_str); + tt_int_op(retval, ==, 200); + } + + /* Again, fetch the published descriptor and perform the revision counter + validation. The revision counter must have changed. */ + { + const ed25519_public_key_t *blinded_key; + + blinded_key = &published_desc->plaintext_data.blinded_pubkey; + received_desc_str = helper_fetch_desc_from_hsdir(blinded_key); + + retval = hs_desc_decode_descriptor(received_desc_str,NULL, &received_desc); + tt_int_op(retval, ==, 0); + tt_assert(received_desc); + + /* Check that the revision counter is the latest */ + tt_u64_op(received_desc->plaintext_data.revision_counter, ==, 1313); + } + + done: + hs_descriptor_free(published_desc); + hs_descriptor_free(received_desc); + tor_free(received_desc_str); + tor_free(published_desc_str); +} + +struct testcase_t hs_cache[] = { + /* Encoding tests. */ + { "directory", test_directory, TT_FORK, + NULL, NULL }, + { "clean_as_dir", test_clean_as_dir, TT_FORK, + NULL, NULL }, + { "hsdir_revision_counter_check", test_hsdir_revision_counter_check, TT_FORK, + NULL, NULL }, + { "upload_and_download_hs_desc", test_upload_and_download_hs_desc, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_descriptor.c b/src/test/test_hs_descriptor.c new file mode 100644 index 0000000000..97fe1910b8 --- /dev/null +++ b/src/test/test_hs_descriptor.c @@ -0,0 +1,1039 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_descriptor.c + * \brief Test hidden service descriptor encoding and decoding. + */ + +#define HS_DESCRIPTOR_PRIVATE + +#include "crypto_ed25519.h" +#include "ed25519_cert.h" +#include "or.h" +#include "hs_descriptor.h" +#include "test.h" +#include "torcert.h" + +static hs_desc_intro_point_t * +helper_build_intro_point(const ed25519_keypair_t *blinded_kp, time_t now, + const char *addr, int legacy) +{ + int ret; + ed25519_keypair_t auth_kp; + hs_desc_intro_point_t *intro_point = NULL; + hs_desc_intro_point_t *ip = tor_malloc_zero(sizeof(*ip)); + ip->link_specifiers = smartlist_new(); + + { + 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; + break; + case AF_INET6: + ls->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); + } + + ret = ed25519_keypair_generate(&auth_kp, 0); + tt_int_op(ret, ==, 0); + ip->auth_key_cert = tor_cert_create(blinded_kp, CERT_TYPE_AUTH_HS_IP_KEY, + &auth_kp.pubkey, now, + HS_DESC_CERT_LIFETIME, + CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(ip->auth_key_cert); + + if (legacy) { + ip->enc_key.legacy = crypto_pk_new(); + ip->enc_key_type = HS_DESC_KEY_TYPE_LEGACY; + tt_assert(ip->enc_key.legacy); + ret = crypto_pk_generate_key(ip->enc_key.legacy); + tt_int_op(ret, ==, 0); + } else { + ret = curve25519_keypair_generate(&ip->enc_key.curve25519, 0); + tt_int_op(ret, ==, 0); + ip->enc_key_type = HS_DESC_KEY_TYPE_CURVE25519; + } + + intro_point = ip; + done: + return intro_point; +} + +/* Return a valid hs_descriptor_t object. If no_ip is set, no introduction + * points are added. */ +static hs_descriptor_t * +helper_build_hs_desc(unsigned int no_ip, ed25519_public_key_t *signing_pubkey) +{ + int ret; + time_t now = time(NULL); + ed25519_keypair_t blinded_kp; + hs_descriptor_t *descp = NULL, *desc = tor_malloc_zero(sizeof(*desc)); + + desc->plaintext_data.version = HS_DESC_SUPPORTED_FORMAT_VERSION_MAX; + + /* Copy only the public key into the descriptor. */ + memcpy(&desc->plaintext_data.signing_pubkey, signing_pubkey, + sizeof(ed25519_public_key_t)); + + ret = ed25519_keypair_generate(&blinded_kp, 0); + tt_int_op(ret, ==, 0); + /* Copy only the public key into the descriptor. */ + memcpy(&desc->plaintext_data.blinded_pubkey, &blinded_kp.pubkey, + sizeof(ed25519_public_key_t)); + + desc->plaintext_data.signing_key_cert = + tor_cert_create(&blinded_kp, CERT_TYPE_SIGNING_HS_DESC, signing_pubkey, + now, 3600, CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(desc->plaintext_data.signing_key_cert); + desc->plaintext_data.revision_counter = 42; + desc->plaintext_data.lifetime_sec = 3 * 60 * 60; + + /* Setup encrypted data section. */ + desc->encrypted_data.create2_ntor = 1; + desc->encrypted_data.auth_types = smartlist_new(); + desc->encrypted_data.single_onion_service = 1; + smartlist_add(desc->encrypted_data.auth_types, tor_strdup("ed25519")); + desc->encrypted_data.intro_points = smartlist_new(); + if (!no_ip) { + /* Add four intro points. */ + smartlist_add(desc->encrypted_data.intro_points, + helper_build_intro_point(&blinded_kp, now, "1.2.3.4", 0)); + smartlist_add(desc->encrypted_data.intro_points, + helper_build_intro_point(&blinded_kp, now, "[2600::1]", 0)); + smartlist_add(desc->encrypted_data.intro_points, + helper_build_intro_point(&blinded_kp, now, "3.2.1.4", 1)); + smartlist_add(desc->encrypted_data.intro_points, + helper_build_intro_point(&blinded_kp, now, "", 1)); + } + + descp = desc; + done: + return descp; +} + +static void +helper_compare_hs_desc(const hs_descriptor_t *desc1, + const hs_descriptor_t *desc2) +{ + char *addr1 = NULL, *addr2 = NULL; + /* Plaintext data section. */ + tt_int_op(desc1->plaintext_data.version, OP_EQ, + desc2->plaintext_data.version); + tt_uint_op(desc1->plaintext_data.lifetime_sec, OP_EQ, + desc2->plaintext_data.lifetime_sec); + tt_assert(tor_cert_eq(desc1->plaintext_data.signing_key_cert, + desc2->plaintext_data.signing_key_cert)); + tt_mem_op(desc1->plaintext_data.signing_pubkey.pubkey, OP_EQ, + desc2->plaintext_data.signing_pubkey.pubkey, + ED25519_PUBKEY_LEN); + tt_mem_op(desc1->plaintext_data.blinded_pubkey.pubkey, OP_EQ, + desc2->plaintext_data.blinded_pubkey.pubkey, + ED25519_PUBKEY_LEN); + tt_u64_op(desc1->plaintext_data.revision_counter, ==, + desc2->plaintext_data.revision_counter); + + /* NOTE: We can't compare the encrypted blob because when encoding the + * descriptor, the object is immutable thus we don't update it with the + * encrypted blob. As contrast to the decoding process where we populate a + * descriptor object. */ + + /* Encrypted data section. */ + tt_uint_op(desc1->encrypted_data.create2_ntor, ==, + desc2->encrypted_data.create2_ntor); + + /* Authentication type. */ + tt_int_op(!!desc1->encrypted_data.auth_types, ==, + !!desc2->encrypted_data.auth_types); + if (desc1->encrypted_data.auth_types && desc2->encrypted_data.auth_types) { + tt_int_op(smartlist_len(desc1->encrypted_data.auth_types), ==, + smartlist_len(desc2->encrypted_data.auth_types)); + for (int i = 0; i < smartlist_len(desc1->encrypted_data.auth_types); i++) { + tt_str_op(smartlist_get(desc1->encrypted_data.auth_types, i), OP_EQ, + smartlist_get(desc2->encrypted_data.auth_types, i)); + } + } + + /* Introduction points. */ + { + tt_assert(desc1->encrypted_data.intro_points); + tt_assert(desc2->encrypted_data.intro_points); + tt_int_op(smartlist_len(desc1->encrypted_data.intro_points), ==, + smartlist_len(desc2->encrypted_data.intro_points)); + for (int i=0; i < smartlist_len(desc1->encrypted_data.intro_points); i++) { + hs_desc_intro_point_t *ip1 = smartlist_get(desc1->encrypted_data + .intro_points, i), + *ip2 = smartlist_get(desc2->encrypted_data + .intro_points, i); + tt_assert(tor_cert_eq(ip1->auth_key_cert, ip2->auth_key_cert)); + tt_int_op(ip1->enc_key_type, OP_EQ, ip2->enc_key_type); + tt_assert(ip1->enc_key_type == HS_DESC_KEY_TYPE_LEGACY || + ip1->enc_key_type == HS_DESC_KEY_TYPE_CURVE25519); + switch (ip1->enc_key_type) { + case HS_DESC_KEY_TYPE_LEGACY: + tt_int_op(crypto_pk_cmp_keys(ip1->enc_key.legacy, ip2->enc_key.legacy), + OP_EQ, 0); + break; + case HS_DESC_KEY_TYPE_CURVE25519: + tt_mem_op(ip1->enc_key.curve25519.pubkey.public_key, OP_EQ, + ip2->enc_key.curve25519.pubkey.public_key, + CURVE25519_PUBKEY_LEN); + break; + } + + tt_int_op(smartlist_len(ip1->link_specifiers), ==, + smartlist_len(ip2->link_specifiers)); + for (int j = 0; j < smartlist_len(ip1->link_specifiers); j++) { + hs_desc_link_specifier_t *ls1 = smartlist_get(ip1->link_specifiers, j), + *ls2 = smartlist_get(ip2->link_specifiers, j); + tt_int_op(ls1->type, ==, ls2->type); + switch (ls1->type) { + case LS_IPV4: + case LS_IPV6: + { + addr1 = tor_addr_to_str_dup(&ls1->u.ap.addr); + addr2 = tor_addr_to_str_dup(&ls2->u.ap.addr); + tt_str_op(addr1, OP_EQ, addr2); + tor_free(addr1); + tor_free(addr2); + tt_int_op(ls1->u.ap.port, ==, ls2->u.ap.port); + } + break; + case LS_LEGACY_ID: + tt_mem_op(ls1->u.legacy_id, OP_EQ, ls2->u.legacy_id, + sizeof(ls1->u.legacy_id)); + break; + default: + /* Unknown type, caught it and print its value. */ + tt_int_op(ls1->type, OP_EQ, -1); + } + } + } + } + + done: + tor_free(addr1); + tor_free(addr2); +} + +/* Test certificate encoding put in a descriptor. */ +static void +test_cert_encoding(void *arg) +{ + int ret; + char *encoded = NULL; + time_t now = time(NULL); + ed25519_keypair_t kp; + ed25519_public_key_t signed_key; + ed25519_secret_key_t secret_key; + tor_cert_t *cert = NULL; + + (void) arg; + + ret = ed25519_keypair_generate(&kp, 0); + tt_int_op(ret, == , 0); + ret = ed25519_secret_key_generate(&secret_key, 0); + tt_int_op(ret, == , 0); + ret = ed25519_public_key_generate(&signed_key, &secret_key); + tt_int_op(ret, == , 0); + + cert = tor_cert_create(&kp, CERT_TYPE_SIGNING_AUTH, &signed_key, + now, 3600 * 2, CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(cert); + + /* Test the certificate encoding function. */ + ret = tor_cert_encode_ed22519(cert, &encoded); + tt_int_op(ret, ==, 0); + + /* Validated the certificate string. */ + { + char *end, *pos = encoded; + char *b64_cert, buf[256]; + size_t b64_cert_len; + tor_cert_t *parsed_cert; + + tt_int_op(strcmpstart(pos, "-----BEGIN ED25519 CERT-----\n"), ==, 0); + pos += strlen("-----BEGIN ED25519 CERT-----\n"); + + /* Isolate the base64 encoded certificate and try to decode it. */ + end = strstr(pos, "-----END ED25519 CERT-----"); + tt_assert(end); + b64_cert = pos; + b64_cert_len = end - pos; + ret = base64_decode(buf, sizeof(buf), b64_cert, b64_cert_len); + tt_int_op(ret, >, 0); + /* Parseable? */ + parsed_cert = tor_cert_parse((uint8_t *) buf, ret); + tt_assert(parsed_cert); + /* Signature is valid? */ + ret = tor_cert_checksig(parsed_cert, &kp.pubkey, now + 10); + tt_int_op(ret, ==, 0); + ret = tor_cert_eq(cert, parsed_cert); + tt_int_op(ret, ==, 1); + /* The cert did have the signing key? */ + ret= ed25519_pubkey_eq(&parsed_cert->signing_key, &kp.pubkey); + tt_int_op(ret, ==, 1); + tor_cert_free(parsed_cert); + + /* Get to the end part of the certificate. */ + pos += b64_cert_len; + tt_int_op(strcmpstart(pos, "-----END ED25519 CERT-----"), ==, 0); + pos += strlen("-----END ED25519 CERT-----"); + } + + done: + tor_cert_free(cert); + tor_free(encoded); +} + +/* Test the descriptor padding. */ +static void +test_descriptor_padding(void *arg) +{ + char *plaintext; + size_t plaintext_len, padded_len; + uint8_t *padded_plaintext = NULL; + +/* Example: if l = 129, the ceiled division gives 2 and then multiplied by 128 + * to give 256. With l = 127, ceiled division gives 1 then times 128. */ +#define PADDING_EXPECTED_LEN(l) \ + CEIL_DIV(l, HS_DESC_PLAINTEXT_PADDING_MULTIPLE) * \ + HS_DESC_PLAINTEXT_PADDING_MULTIPLE + + (void) arg; + + { /* test #1: no padding */ + plaintext_len = HS_DESC_PLAINTEXT_PADDING_MULTIPLE; + plaintext = tor_malloc(plaintext_len); + padded_len = build_plaintext_padding(plaintext, plaintext_len, + &padded_plaintext); + tt_assert(padded_plaintext); + tor_free(plaintext); + /* Make sure our padding has been zeroed. */ + tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len, + padded_len - plaintext_len), OP_EQ, 1); + tor_free(padded_plaintext); + /* Never never have a padded length smaller than the plaintext. */ + tt_int_op(padded_len, OP_GE, plaintext_len); + tt_int_op(padded_len, OP_EQ, PADDING_EXPECTED_LEN(plaintext_len)); + } + + { /* test #2: one byte padding? */ + plaintext_len = HS_DESC_PLAINTEXT_PADDING_MULTIPLE - 1; + plaintext = tor_malloc(plaintext_len); + padded_plaintext = NULL; + padded_len = build_plaintext_padding(plaintext, plaintext_len, + &padded_plaintext); + tt_assert(padded_plaintext); + tor_free(plaintext); + /* Make sure our padding has been zeroed. */ + tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len, + padded_len - plaintext_len), OP_EQ, 1); + tor_free(padded_plaintext); + /* Never never have a padded length smaller than the plaintext. */ + tt_int_op(padded_len, OP_GE, plaintext_len); + tt_int_op(padded_len, OP_EQ, PADDING_EXPECTED_LEN(plaintext_len)); + } + + { /* test #3: Lots more bytes of padding? */ + plaintext_len = HS_DESC_PLAINTEXT_PADDING_MULTIPLE + 1; + plaintext = tor_malloc(plaintext_len); + padded_plaintext = NULL; + padded_len = build_plaintext_padding(plaintext, plaintext_len, + &padded_plaintext); + tt_assert(padded_plaintext); + tor_free(plaintext); + /* Make sure our padding has been zeroed. */ + tt_int_op(tor_mem_is_zero((char *) padded_plaintext + plaintext_len, + padded_len - plaintext_len), OP_EQ, 1); + tor_free(padded_plaintext); + /* Never never have a padded length smaller than the plaintext. */ + tt_int_op(padded_len, OP_GE, plaintext_len); + tt_int_op(padded_len, OP_EQ, PADDING_EXPECTED_LEN(plaintext_len)); + } + + done: + return; +} + +static void +test_link_specifier(void *arg) +{ + ssize_t ret; + hs_desc_link_specifier_t spec; + smartlist_t *link_specifiers = smartlist_new(); + + (void) arg; + + /* Always this port. */ + spec.u.ap.port = 42; + smartlist_add(link_specifiers, &spec); + + /* Test IPv4 for starter. */ + { + char *b64, buf[256]; + uint32_t ipv4; + link_specifier_t *ls; + + spec.type = LS_IPV4; + ret = tor_addr_parse(&spec.u.ap.addr, "1.2.3.4"); + tt_int_op(ret, ==, AF_INET); + b64 = encode_link_specifiers(link_specifiers); + tt_assert(b64); + + /* Decode it and validate the format. */ + ret = base64_decode(buf, sizeof(buf), b64, strlen(b64)); + tt_int_op(ret, >, 0); + /* First byte is the number of link specifier. */ + tt_int_op(get_uint8(buf), ==, 1); + ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1); + tt_int_op(ret, ==, 8); + /* Should be 2 bytes for port and 4 bytes for IPv4. */ + tt_int_op(link_specifier_get_ls_len(ls), ==, 6); + ipv4 = link_specifier_get_un_ipv4_addr(ls); + tt_int_op(tor_addr_to_ipv4h(&spec.u.ap.addr), ==, ipv4); + tt_int_op(link_specifier_get_un_ipv4_port(ls), ==, spec.u.ap.port); + + link_specifier_free(ls); + tor_free(b64); + } + + /* Test IPv6. */ + { + char *b64, buf[256]; + uint8_t ipv6[16]; + link_specifier_t *ls; + + spec.type = LS_IPV6; + ret = tor_addr_parse(&spec.u.ap.addr, "[1:2:3:4::]"); + tt_int_op(ret, ==, AF_INET6); + b64 = encode_link_specifiers(link_specifiers); + tt_assert(b64); + + /* Decode it and validate the format. */ + ret = base64_decode(buf, sizeof(buf), b64, strlen(b64)); + tt_int_op(ret, >, 0); + /* First byte is the number of link specifier. */ + tt_int_op(get_uint8(buf), ==, 1); + ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1); + tt_int_op(ret, ==, 20); + /* Should be 2 bytes for port and 16 bytes for IPv6. */ + tt_int_op(link_specifier_get_ls_len(ls), ==, 18); + for (unsigned int i = 0; i < sizeof(ipv6); i++) { + ipv6[i] = link_specifier_get_un_ipv6_addr(ls, i); + } + tt_mem_op(tor_addr_to_in6_addr8(&spec.u.ap.addr), ==, ipv6, sizeof(ipv6)); + tt_int_op(link_specifier_get_un_ipv6_port(ls), ==, spec.u.ap.port); + + link_specifier_free(ls); + tor_free(b64); + } + + /* Test legacy. */ + { + char *b64, buf[256]; + uint8_t *id; + link_specifier_t *ls; + + spec.type = LS_LEGACY_ID; + memset(spec.u.legacy_id, 'Y', sizeof(spec.u.legacy_id)); + b64 = encode_link_specifiers(link_specifiers); + tt_assert(b64); + + /* Decode it and validate the format. */ + ret = base64_decode(buf, sizeof(buf), b64, strlen(b64)); + tt_int_op(ret, >, 0); + /* First byte is the number of link specifier. */ + tt_int_op(get_uint8(buf), ==, 1); + ret = link_specifier_parse(&ls, (uint8_t *) buf + 1, ret - 1); + /* 20 bytes digest + 1 byte type + 1 byte len. */ + tt_int_op(ret, ==, 22); + tt_int_op(link_specifier_getlen_un_legacy_id(ls), OP_EQ, DIGEST_LEN); + /* Digest length is 20 bytes. */ + tt_int_op(link_specifier_get_ls_len(ls), OP_EQ, DIGEST_LEN); + id = link_specifier_getarray_un_legacy_id(ls); + tt_mem_op(spec.u.legacy_id, OP_EQ, id, DIGEST_LEN); + + link_specifier_free(ls); + tor_free(b64); + } + + done: + smartlist_free(link_specifiers); +} + +static void +test_encode_descriptor(void *arg) +{ + int ret; + char *encoded = NULL; + ed25519_keypair_t signing_kp; + hs_descriptor_t *desc = NULL; + + (void) arg; + + ret = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(ret, ==, 0); + desc = helper_build_hs_desc(0, &signing_kp.pubkey); + ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded); + tt_int_op(ret, ==, 0); + tt_assert(encoded); + + done: + hs_descriptor_free(desc); + tor_free(encoded); +} + +static void +test_decode_descriptor(void *arg) +{ + int ret; + char *encoded = NULL; + ed25519_keypair_t signing_kp; + hs_descriptor_t *desc = NULL; + hs_descriptor_t *decoded = NULL; + hs_descriptor_t *desc_no_ip = NULL; + + (void) arg; + + ret = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(ret, ==, 0); + desc = helper_build_hs_desc(0, &signing_kp.pubkey); + + /* Give some bad stuff to the decoding function. */ + ret = hs_desc_decode_descriptor("hladfjlkjadf", NULL, &decoded); + tt_int_op(ret, OP_EQ, -1); + + ret = hs_desc_encode_descriptor(desc, &signing_kp, &encoded); + tt_int_op(ret, ==, 0); + tt_assert(encoded); + + ret = hs_desc_decode_descriptor(encoded, NULL, &decoded); + tt_int_op(ret, ==, 0); + tt_assert(decoded); + + helper_compare_hs_desc(desc, decoded); + + /* Decode a descriptor with _no_ introduction points. */ + { + ed25519_keypair_t signing_kp_no_ip; + ret = ed25519_keypair_generate(&signing_kp_no_ip, 0); + tt_int_op(ret, ==, 0); + desc_no_ip = helper_build_hs_desc(1, &signing_kp_no_ip.pubkey); + tt_assert(desc_no_ip); + tor_free(encoded); + ret = hs_desc_encode_descriptor(desc_no_ip, &signing_kp_no_ip, &encoded); + tt_int_op(ret, ==, 0); + tt_assert(encoded); + hs_descriptor_free(decoded); + ret = hs_desc_decode_descriptor(encoded, NULL, &decoded); + tt_int_op(ret, ==, 0); + tt_assert(decoded); + } + + done: + hs_descriptor_free(desc); + hs_descriptor_free(desc_no_ip); + hs_descriptor_free(decoded); + tor_free(encoded); +} + +static void +test_supported_version(void *arg) +{ + int ret; + + (void) arg; + + /* Unsupported. */ + ret = hs_desc_is_supported_version(42); + tt_int_op(ret, OP_EQ, 0); + /* To early. */ + ret = hs_desc_is_supported_version(HS_DESC_SUPPORTED_FORMAT_VERSION_MIN - 1); + tt_int_op(ret, OP_EQ, 0); + /* One too new. */ + ret = hs_desc_is_supported_version(HS_DESC_SUPPORTED_FORMAT_VERSION_MAX + 1); + tt_int_op(ret, OP_EQ, 0); + /* Valid version. */ + ret = hs_desc_is_supported_version(3); + tt_int_op(ret, OP_EQ, 1); + + done: + ; +} + +static void +test_encrypted_data_len(void *arg) +{ + int ret; + size_t value; + + (void) arg; + + /* No length, error. */ + ret = encrypted_data_length_is_valid(0); + tt_int_op(ret, OP_EQ, 0); + /* This value is missing data. */ + value = HS_DESC_ENCRYPTED_SALT_LEN + DIGEST256_LEN; + ret = encrypted_data_length_is_valid(value); + tt_int_op(ret, OP_EQ, 0); + /* Valid value. */ + value = HS_DESC_PADDED_PLAINTEXT_MAX_LEN + HS_DESC_ENCRYPTED_SALT_LEN + + DIGEST256_LEN; + ret = encrypted_data_length_is_valid(value); + tt_int_op(ret, OP_EQ, 1); + + /* XXX: Test maximum possible size. */ + + done: + ; +} + +static void +test_decode_intro_point(void *arg) +{ + int ret; + char *encoded_ip = NULL; + size_t len_out; + hs_desc_intro_point_t *ip = NULL; + ed25519_keypair_t signing_kp; + hs_descriptor_t *desc = NULL; + + (void) arg; + + /* The following certificate expires in 2036. After that, one of the test + * will fail because of the expiry time. */ + + /* Seperate pieces of a valid encoded introduction point. */ + const char *intro_point = + "introduction-point AQIUMDI5OUYyNjhGQ0E5RDU1Q0QxNTc="; + const char *auth_key = + "auth-key\n" + "-----BEGIN ED25519 CERT-----\n" + "AQkACOhAAQW8ltYZMIWpyrfyE/b4Iyi8CNybCwYs6ADk7XfBaxsFAQAgBAD3/BE4\n" + "XojGE/N2bW/wgnS9r2qlrkydGyuCKIGayYx3haZ39LD4ZTmSMRxwmplMAqzG/XNP\n" + "0Kkpg4p2/VnLFJRdU1SMFo1lgQ4P0bqw7Tgx200fulZ4KUM5z5V7m+a/mgY=\n" + "-----END ED25519 CERT-----"; + const char *enc_key = + "enc-key ntor bpZKLsuhxP6woDQ3yVyjm5gUKSk7RjfAijT2qrzbQk0="; + const char *enc_key_legacy = + "enc-key legacy\n" + "-----BEGIN RSA PUBLIC KEY-----\n" + "MIGJAoGBAO4bATcW8kW4h6RQQAKEgg+aXCpF4JwbcO6vGZtzXTDB+HdPVQzwqkbh\n" + "XzFM6VGArhYw4m31wcP1Z7IwULir7UMnAFd7Zi62aYfU6l+Y1yAoZ1wzu1XBaAMK\n" + "ejpwQinW9nzJn7c2f69fVke3pkhxpNdUZ+vplSA/l9iY+y+v+415AgMBAAE=\n" + "-----END RSA PUBLIC KEY-----"; + const char *enc_key_cert = + "enc-key-certification\n" + "-----BEGIN ED25519 CERT-----\n" + "AQsACOhZAUpNvCZ1aJaaR49lS6MCdsVkhVGVrRqoj0Y2T4SzroAtAQAgBABFOcGg\n" + "lbTt1DF5nKTE/gU3Fr8ZtlCIOhu1A+F5LM7fqCUupfesg0KTHwyIZOYQbJuM5/he\n" + "/jDNyLy9woPJdjkxywaY2RPUxGjLYtMQV0E8PUxWyICV+7y52fTCYaKpYQw=\n" + "-----END ED25519 CERT-----"; + const char *enc_key_cert_legacy = + "enc-key-certification\n" + "-----BEGIN CROSSCERT-----\n" + "Sk28JnVolppHj2VLowJ2xWSFUZWtGqiPRjZPhLOugC0ACOhZgFPA5egeRDUXMM1U\n" + "Fn3c7Je0gJS6mVma5FzwlgwggeriF13UZcaT71vEAN/ZJXbxOfQVGMZ0rXuFpjUq\n" + "C8CvqmZIwEUaPE1nDFtmnTcucvNS1YQl9nsjH3ejbxc+4yqps/cXh46FmXsm5yz7\n" + "NZjBM9U1fbJhlNtOvrkf70K8bLk6\n" + "-----END CROSSCERT-----"; + + (void) enc_key_legacy; + (void) enc_key_cert_legacy; + + /* Start by testing the "decode all intro points" function. */ + { + char *line; + ret = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(ret, ==, 0); + desc = helper_build_hs_desc(0, &signing_kp.pubkey); + tt_assert(desc); + /* Only try to decode an incomplete introduction point section. */ + tor_asprintf(&line, "\n%s", intro_point); + ret = decode_intro_points(desc, &desc->encrypted_data, line); + tor_free(line); + tt_int_op(ret, ==, -1); + + /* Decode one complete intro point. */ + smartlist_t *lines = smartlist_new(); + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) enc_key); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + tor_asprintf(&line, "\n%s", encoded_ip); + tor_free(encoded_ip); + ret = decode_intro_points(desc, &desc->encrypted_data, line); + tor_free(line); + smartlist_free(lines); + tt_int_op(ret, ==, 0); + } + + /* Try to decode a junk string. */ + { + hs_descriptor_free(desc); + desc = NULL; + ret = ed25519_keypair_generate(&signing_kp, 0); + tt_int_op(ret, ==, 0); + desc = helper_build_hs_desc(0, &signing_kp.pubkey); + const char *junk = "this is not a descriptor"; + ip = decode_introduction_point(desc, junk); + tt_assert(!ip); + desc_intro_point_free(ip); + ip = NULL; + } + + /* Invalid link specifiers. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = "introduction-point blah"; + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) enc_key); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + desc_intro_point_free(ip); + ip = NULL; + } + + /* Invalid auth key type. */ + { + smartlist_t *lines = smartlist_new(); + /* Try to put a valid object that our tokenize function will be able to + * parse but that has nothing to do with the auth_key. */ + const char *bad_line = + "auth-key\n" + "-----BEGIN UNICORN CERT-----\n" + "MIGJAoGBAO4bATcW8kW4h6RQQAKEgg+aXCpF4JwbcO6vGZtzXTDB+HdPVQzwqkbh\n" + "XzFM6VGArhYw4m31wcP1Z7IwULir7UMnAFd7Zi62aYfU6l+Y1yAoZ1wzu1XBaAMK\n" + "ejpwQinW9nzJn7c2f69fVke3pkhxpNdUZ+vplSA/l9iY+y+v+415AgMBAAE=\n" + "-----END UNICORN CERT-----"; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Invalid enc-key. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = + "enc-key unicorn bpZKLsuhxP6woDQ3yVyjm5gUKSk7RjfAijT2qrzbQk0="; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Invalid enc-key object. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = "enc-key ntor"; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Invalid enc-key base64 curv25519 key. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = "enc-key ntor blah==="; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Invalid enc-key invalid legacy. */ + { + smartlist_t *lines = smartlist_new(); + const char *bad_line = "enc-key legacy blah==="; + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) bad_line); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(!ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + /* Valid object. */ + { + smartlist_t *lines = smartlist_new(); + /* Build intro point text. */ + smartlist_add(lines, (char *) intro_point); + smartlist_add(lines, (char *) auth_key); + smartlist_add(lines, (char *) enc_key); + smartlist_add(lines, (char *) enc_key_cert); + encoded_ip = smartlist_join_strings(lines, "\n", 0, &len_out); + tt_assert(encoded_ip); + ip = decode_introduction_point(desc, encoded_ip); + tt_assert(ip); + tor_free(encoded_ip); + smartlist_free(lines); + } + + done: + hs_descriptor_free(desc); + desc_intro_point_free(ip); +} + +static void +test_decode_plaintext(void *arg) +{ + int ret; + hs_desc_plaintext_data_t desc_plaintext; + const char *bad_value = "unicorn"; + + (void) arg; + +#define template \ + "hs-descriptor %s\n" \ + "descriptor-lifetime %s\n" \ + "descriptor-signing-key-cert\n" \ + "-----BEGIN ED25519 CERT-----\n" \ + "AQgABjvPAQaG3g+dc6oV/oJV4ODAtkvx56uBnPtBT9mYVuHVOhn7AQAgBABUg3mQ\n" \ + "myBr4bu5LCr53wUEbW2EXui01CbUgU7pfo9LvJG3AcXRojj6HlfsUs9BkzYzYdjF\n" \ + "A69Apikgu0ewHYkFFASt7Il+gB3w6J8YstQJZT7dtbtl+doM7ug8B68Qdg8=\n" \ + "-----END ED25519 CERT-----\n" \ + "revision-counter %s\n" \ + "encrypted\n" \ + "-----BEGIN %s-----\n" \ + "UNICORN\n" \ + "-----END MESSAGE-----\n" \ + "signature m20WJH5agqvwhq7QeuEZ1mYyPWQDO+eJOZUjLhAiKu8DbL17DsDfJE6kXbWy" \ + "HimbNj2we0enV3cCOOAsmPOaAw\n" + + /* Invalid version. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, bad_value, "180", "42", "MESSAGE"); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Missing fields. */ + { + const char *plaintext = "hs-descriptor 3\n"; + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Max length. */ + { + size_t big = 64000; + /* Must always be bigger than HS_DESC_MAX_LEN. */ + tt_int_op(HS_DESC_MAX_LEN, <, big); + char *plaintext = tor_malloc_zero(big); + memset(plaintext, 'a', big); + plaintext[big - 1] = '\0'; + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Bad lifetime value. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, "3", bad_value, "42", "MESSAGE"); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Huge lifetime value. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, "3", "7181615", "42", "MESSAGE"); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Invalid encrypted section. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, "3", "180", "42", bad_value); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + /* Invalid revision counter. */ + { + char *plaintext; + tor_asprintf(&plaintext, template, "3", "180", bad_value, "MESSAGE"); + ret = hs_desc_decode_plaintext(plaintext, &desc_plaintext); + tor_free(plaintext); + tt_int_op(ret, OP_EQ, -1); + } + + done: + ; +} + +static void +test_validate_cert(void *arg) +{ + int ret; + time_t now = time(NULL); + ed25519_keypair_t kp; + tor_cert_t *cert = NULL; + + (void) arg; + + ret = ed25519_keypair_generate(&kp, 0); + tt_int_op(ret, ==, 0); + + /* Cert of type CERT_TYPE_AUTH_HS_IP_KEY. */ + cert = tor_cert_create(&kp, CERT_TYPE_AUTH_HS_IP_KEY, + &kp.pubkey, now, 3600, + CERT_FLAG_INCLUDE_SIGNING_KEY); + tt_assert(cert); + /* Test with empty certificate. */ + ret = cert_is_valid(NULL, CERT_TYPE_AUTH_HS_IP_KEY, "unicorn"); + tt_int_op(ret, OP_EQ, 0); + /* Test with a bad type. */ + ret = cert_is_valid(cert, CERT_TYPE_SIGNING_HS_DESC, "unicorn"); + tt_int_op(ret, OP_EQ, 0); + /* Normal validation. */ + ret = cert_is_valid(cert, CERT_TYPE_AUTH_HS_IP_KEY, "unicorn"); + tt_int_op(ret, OP_EQ, 1); + /* Break signing key so signature verification will fails. */ + memset(&cert->signing_key, 0, sizeof(cert->signing_key)); + ret = cert_is_valid(cert, CERT_TYPE_AUTH_HS_IP_KEY, "unicorn"); + tt_int_op(ret, OP_EQ, 0); + tor_cert_free(cert); + + /* Try a cert without including the signing key. */ + cert = tor_cert_create(&kp, CERT_TYPE_AUTH_HS_IP_KEY, &kp.pubkey, now, + 3600, 0); + tt_assert(cert); + /* Test with a bad type. */ + ret = cert_is_valid(cert, CERT_TYPE_AUTH_HS_IP_KEY, "unicorn"); + tt_int_op(ret, OP_EQ, 0); + + done: + tor_cert_free(cert); +} + +static void +test_desc_signature(void *arg) +{ + int ret; + char *data = NULL, *desc = NULL; + char sig_b64[ED25519_SIG_BASE64_LEN + 1]; + ed25519_keypair_t kp; + ed25519_signature_t sig; + + (void) arg; + + ed25519_keypair_generate(&kp, 0); + /* Setup a phoony descriptor but with a valid signature token that is the + * signature is verifiable. */ + tor_asprintf(&data, "This is a signed descriptor\n"); + ret = ed25519_sign_prefixed(&sig, (const uint8_t *) data, strlen(data), + "Tor onion service descriptor sig v3", &kp); + tt_int_op(ret, ==, 0); + ret = ed25519_signature_to_base64(sig_b64, &sig); + tt_int_op(ret, ==, 0); + /* Build the descriptor that should be valid. */ + tor_asprintf(&desc, "%ssignature %s\n", data, sig_b64); + ret = desc_sig_is_valid(sig_b64, &kp.pubkey, desc, strlen(desc)); + tt_int_op(ret, ==, 1); + /* Junk signature. */ + ret = desc_sig_is_valid("JUNK", &kp.pubkey, desc, strlen(desc)); + tt_int_op(ret, ==, 0); + + done: + tor_free(desc); + tor_free(data); +} + +struct testcase_t hs_descriptor[] = { + /* Encoding tests. */ + { "cert_encoding", test_cert_encoding, TT_FORK, + NULL, NULL }, + { "link_specifier", test_link_specifier, TT_FORK, + NULL, NULL }, + { "encode_descriptor", test_encode_descriptor, TT_FORK, + NULL, NULL }, + { "descriptor_padding", test_descriptor_padding, TT_FORK, + NULL, NULL }, + + /* Decoding tests. */ + { "decode_descriptor", test_decode_descriptor, TT_FORK, + NULL, NULL }, + { "encrypted_data_len", test_encrypted_data_len, TT_FORK, + NULL, NULL }, + { "decode_intro_point", test_decode_intro_point, TT_FORK, + NULL, NULL }, + { "decode_plaintext", test_decode_plaintext, TT_FORK, + NULL, NULL }, + + /* Misc. */ + { "version", test_supported_version, TT_FORK, + NULL, NULL }, + { "validate_cert", test_validate_cert, TT_FORK, + NULL, NULL }, + { "desc_signature", test_desc_signature, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_intropoint.c b/src/test/test_hs_intropoint.c new file mode 100644 index 0000000000..b207cd4ce3 --- /dev/null +++ b/src/test/test_hs_intropoint.c @@ -0,0 +1,875 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_service.c + * \brief Test hidden service functionality. + */ + +#define HS_SERVICE_PRIVATE +#define HS_INTROPOINT_PRIVATE +#define RENDSERVICE_PRIVATE +#define CIRCUITLIST_PRIVATE + +#include "test.h" +#include "log_test_helpers.h" +#include "crypto.h" +#include "log_test_helpers.h" + +#include "or.h" +#include "ht.h" + +/* Trunnel. */ +#include "hs/cell_establish_intro.h" +#include "hs/cell_introduce1.h" +#include "hs/cell_common.h" +#include "hs_service.h" +#include "hs_common.h" +#include "hs_circuitmap.h" +#include "hs_intropoint.h" + +#include "circuitlist.h" +#include "circuituse.h" +#include "rendservice.h" +#include "relay.h" + +/* Mock function to avoid networking in unittests */ +static int +mock_send_intro_established_cell(or_circuit_t *circ) +{ + (void) circ; + return 0; +} + +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; + return 0; +} + +static or_circuit_t * +helper_create_intro_circuit(void) +{ + or_circuit_t *circ = or_circuit_new(0, NULL); + tt_assert(circ); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + done: + return circ; +} + +static hs_cell_introduce1_t * +helper_create_introduce1_cell(void) +{ + hs_cell_introduce1_t *cell = NULL; + ed25519_keypair_t auth_key_kp; + + /* Generate the auth_key of the cell. */ + if (ed25519_keypair_generate(&auth_key_kp, 0) < 0) { + goto err; + } + + cell = hs_cell_introduce1_new(); + tt_assert(cell); + + /* Set the auth key. */ + { + size_t auth_key_len = sizeof(auth_key_kp.pubkey); + hs_cell_introduce1_set_auth_key_type(cell, + HS_INTRO_AUTH_KEY_TYPE_ED25519); + hs_cell_introduce1_set_auth_key_len(cell, auth_key_len); + hs_cell_introduce1_setlen_auth_key(cell, auth_key_len); + uint8_t *auth_key_ptr = hs_cell_introduce1_getarray_auth_key(cell); + memcpy(auth_key_ptr, auth_key_kp.pubkey.pubkey, auth_key_len); + } + + /* Set the cell extentions to none. */ + { + cell_extension_t *ext = cell_extension_new(); + cell_extension_set_num(ext, 0); + hs_cell_introduce1_set_extensions(cell, ext); + } + + /* Set the encrypted section to some data. */ + { + size_t enc_len = 128; + hs_cell_introduce1_setlen_encrypted(cell, enc_len); + uint8_t *enc_ptr = hs_cell_introduce1_getarray_encrypted(cell); + memset(enc_ptr, 'a', enc_len); + } + + return cell; + err: + done: + hs_cell_introduce1_free(cell); + return NULL; +} + +/* Try sending an ESTABLISH_INTRO cell on a circuit that is already an intro + * point. Should fail. */ +static void +test_establish_intro_wrong_purpose(void *arg) +{ + int retval; + hs_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + memcpy(intro_circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN); + + /* Set a bad circuit purpose!! :) */ + circuit_change_purpose(TO_CIRCUIT(intro_circ), CIRCUIT_PURPOSE_INTRO_POINT); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("Rejecting ESTABLISH_INTRO on non-OR circuit."); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + hs_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Prepare a circuit for accepting an ESTABLISH_INTRO cell */ +static void +helper_prepare_circ_for_intro(or_circuit_t *circ, + uint8_t *circuit_key_material) +{ + /* Prepare the circuit for the incoming ESTABLISH_INTRO */ + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + memcpy(circ->rend_circ_nonce, circuit_key_material, DIGEST_LEN); +} + +/* Send an empty ESTABLISH_INTRO cell. Should fail. */ +static void +test_establish_intro_wrong_keytype(void *arg) +{ + int retval; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, (uint8_t*)"", 0); + expect_log_msg_containing("Empty ESTABLISH_INTRO cell."); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send an ESTABLISH_INTRO cell with an unknown auth key type. Should fail. */ +static void +test_establish_intro_wrong_keytype2(void *arg) +{ + int retval; + hs_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Mutate the auth key type! :) */ + cell_body[0] = 42; + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("Unrecognized AUTH_KEY_TYPE 42."); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + hs_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send a legit ESTABLISH_INTRO cell but with a wrong MAC. Should fail. */ +static void +test_establish_intro_wrong_mac(void *arg) +{ + int retval; + hs_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + /* Mangle one byte of the MAC. */ + uint8_t *handshake_ptr = + hs_cell_establish_intro_getarray_handshake_mac(establish_intro_cell); + handshake_ptr[TRUNNEL_SHA3_256_LEN - 1]++; + /* We need to resign the payload with that change. */ + { + ed25519_signature_t sig; + ed25519_keypair_t key_struct; + /* New keypair for the signature since we don't have access to the private + * key material generated earlier when creating the cell. */ + retval = ed25519_keypair_generate(&key_struct, 0); + tt_int_op(retval, OP_EQ, 0); + uint8_t *auth_key_ptr = + hs_cell_establish_intro_getarray_auth_key(establish_intro_cell); + memcpy(auth_key_ptr, key_struct.pubkey.pubkey, ED25519_PUBKEY_LEN); + /* Encode payload so we can sign it. */ + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + retval = ed25519_sign_prefixed(&sig, cell_body, + cell_len - + (ED25519_SIG_LEN + + sizeof(establish_intro_cell->sig_len)), + ESTABLISH_INTRO_SIG_PREFIX, &key_struct); + tt_int_op(retval, OP_EQ, 0); + /* And write the signature to the cell */ + uint8_t *sig_ptr = + hs_cell_establish_intro_getarray_sig(establish_intro_cell); + memcpy(sig_ptr, sig.sig, establish_intro_cell->sig_len); + /* Re-encode with the new signature. */ + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + } + + /* Receive the cell. Should fail because our MAC is wrong. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("ESTABLISH_INTRO handshake_auth not as expected"); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + hs_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send a legit ESTABLISH_INTRO cell but with a wrong auth key length. Should + * fail. */ +static void +test_establish_intro_wrong_auth_key_len(void *arg) +{ + int retval; + hs_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + size_t bad_auth_key_len = ED25519_PUBKEY_LEN - 1; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + /* Mangle the auth key length. */ + hs_cell_establish_intro_set_auth_key_len(establish_intro_cell, + bad_auth_key_len); + hs_cell_establish_intro_setlen_auth_key(establish_intro_cell, + bad_auth_key_len); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("ESTABLISH_INTRO auth key length is invalid"); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + hs_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send a legit ESTABLISH_INTRO cell but with a wrong sig length. Should + * fail. */ +static void +test_establish_intro_wrong_sig_len(void *arg) +{ + int retval; + hs_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + size_t bad_sig_len = ED25519_SIG_LEN - 1; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + /* Mangle the signature length. */ + hs_cell_establish_intro_set_sig_len(establish_intro_cell, bad_sig_len); + hs_cell_establish_intro_setlen_sig(establish_intro_cell, bad_sig_len); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("ESTABLISH_INTRO sig len is invalid"); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + hs_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Send a legit ESTABLISH_INTRO cell but slightly change the signature. Should + * fail. */ +static void +test_establish_intro_wrong_sig(void *arg) +{ + int retval; + hs_cell_establish_intro_t *establish_intro_cell = NULL; + or_circuit_t *intro_circ = or_circuit_new(0,NULL);; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + (void)arg; + + /* Get the auth key of the intro point */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Mutate the last byte (signature)! :) */ + cell_body[cell_len-1]++; + + /* Receive the cell. Should fail. */ + setup_full_capture_of_logs(LOG_INFO); + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + expect_log_msg_containing("Failed to verify ESTABLISH_INTRO cell."); + teardown_capture_of_logs(); + tt_int_op(retval, ==, -1); + + done: + hs_cell_establish_intro_free(establish_intro_cell); + circuit_free(TO_CIRCUIT(intro_circ)); +} + +/* Helper function: Send a well-formed v3 ESTABLISH_INTRO cell to + * <b>intro_circ</b>. Return the cell. */ +static hs_cell_establish_intro_t * +helper_establish_intro_v3(or_circuit_t *intro_circ) +{ + int retval; + hs_cell_establish_intro_t *establish_intro_cell = NULL; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + tt_assert(intro_circ); + + /* Prepare the circuit for the incoming ESTABLISH_INTRO */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + establish_intro_cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(establish_intro_cell); + cell_len = get_establish_intro_payload(cell_body, sizeof(cell_body), + establish_intro_cell); + tt_int_op(cell_len, >, 0); + + /* Receive the cell */ + retval = hs_intro_received_establish_intro(intro_circ, cell_body, cell_len); + tt_int_op(retval, ==, 0); + + done: + return establish_intro_cell; +} + +/* Helper function: Send a well-formed v2 ESTABLISH_INTRO cell to + * <b>intro_circ</b>. Return the public key advertised in the cell. */ +static crypto_pk_t * +helper_establish_intro_v2(or_circuit_t *intro_circ) +{ + crypto_pk_t *key1 = NULL; + int retval; + uint8_t cell_body[RELAY_PAYLOAD_SIZE]; + ssize_t cell_len = 0; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + tt_assert(intro_circ); + + /* Prepare the circuit for the incoming ESTABLISH_INTRO */ + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + helper_prepare_circ_for_intro(intro_circ, circuit_key_material); + + /* Send legacy establish_intro */ + key1 = pk_generate(0); + + /* Use old circuit_key_material why not */ + cell_len = encode_establish_intro_cell_legacy((char*)cell_body, + sizeof(cell_body), + key1, + (char *) circuit_key_material); + tt_int_op(cell_len, >, 0); + + /* Receive legacy establish_intro */ + retval = hs_intro_received_establish_intro(intro_circ, + cell_body, cell_len); + tt_int_op(retval, ==, 0); + + done: + return key1; +} + +/** Successfuly register a v2 intro point and a v3 intro point. Ensure that HS + * circuitmap is maintained properly. */ +static void +test_intro_point_registration(void *arg) +{ + int retval; + hs_circuitmap_ht *the_hs_circuitmap = NULL; + + or_circuit_t *intro_circ = NULL; + hs_cell_establish_intro_t *establish_intro_cell = NULL; + ed25519_public_key_t auth_key; + + crypto_pk_t *legacy_auth_key = NULL; + or_circuit_t *legacy_intro_circ = NULL; + + or_circuit_t *returned_intro_circ = NULL; + + (void) arg; + + MOCK(hs_intro_send_intro_established_cell, mock_send_intro_established_cell); + + hs_circuitmap_init(); + + /* Check that the circuitmap is currently empty */ + { + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(the_hs_circuitmap); + tt_int_op(0, ==, HT_SIZE(the_hs_circuitmap)); + /* Do a circuitmap query in any case */ + returned_intro_circ = hs_circuitmap_get_intro_circ_v3(&auth_key); + tt_ptr_op(returned_intro_circ, ==, NULL); + } + + /* Create a v3 intro point */ + { + intro_circ = or_circuit_new(0, NULL); + tt_assert(intro_circ); + establish_intro_cell = helper_establish_intro_v3(intro_circ); + + /* Check that the intro point was registered on the HS circuitmap */ + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(the_hs_circuitmap); + tt_int_op(1, ==, HT_SIZE(the_hs_circuitmap)); + get_auth_key_from_cell(&auth_key, RELAY_COMMAND_ESTABLISH_INTRO, + establish_intro_cell); + returned_intro_circ = hs_circuitmap_get_intro_circ_v3(&auth_key); + tt_ptr_op(intro_circ, ==, returned_intro_circ); + } + + /* Create a v2 intro point */ + { + char key_digest[DIGEST_LEN]; + + legacy_intro_circ = or_circuit_new(1, NULL); + tt_assert(legacy_intro_circ); + legacy_auth_key = helper_establish_intro_v2(legacy_intro_circ); + tt_assert(legacy_auth_key); + + /* Check that the circuitmap now has two elements */ + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(the_hs_circuitmap); + tt_int_op(2, ==, HT_SIZE(the_hs_circuitmap)); + + /* Check that the new element is our legacy intro circuit. */ + retval = crypto_pk_get_digest(legacy_auth_key, key_digest); + tt_int_op(retval, ==, 0); + returned_intro_circ= hs_circuitmap_get_intro_circ_v2((uint8_t*)key_digest); + tt_ptr_op(legacy_intro_circ, ==, returned_intro_circ); + } + + /* XXX Continue test and try to register a second v3 intro point with the + * same auth key. Make sure that old intro circuit gets closed. */ + + done: + crypto_pk_free(legacy_auth_key); + circuit_free(TO_CIRCUIT(intro_circ)); + circuit_free(TO_CIRCUIT(legacy_intro_circ)); + hs_cell_establish_intro_free(establish_intro_cell); + + { /* Test circuitmap free_all function. */ + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(the_hs_circuitmap); + hs_circuitmap_free_all(); + the_hs_circuitmap = get_hs_circuitmap(); + tt_assert(!the_hs_circuitmap); + } + + UNMOCK(hs_intro_send_intro_established_cell); +} + +static void +test_introduce1_suitable_circuit(void *arg) +{ + int ret; + or_circuit_t *circ = NULL; + + (void) arg; + + /* Valid suitable circuit. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 1); + } + + /* Test if the circuit purpose safeguard works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_INTRO_POINT); + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Test the non-edge circuit safeguard works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + /* Bogus pointer, the check is against NULL on n_chan. */ + circ->base_.n_chan = (channel_t *) circ; + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Mangle the circuit a bit more so see if our only one INTRODUCE1 cell + * limit works correctly. */ + { + circ = or_circuit_new(0, NULL); + circuit_change_purpose(TO_CIRCUIT(circ), CIRCUIT_PURPOSE_OR); + circ->already_received_introduce1 = 1; + ret = circuit_is_suitable_for_introduce1(circ); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, 0); + } + + done: + ; +} + +static void +test_introduce1_is_legacy(void *arg) +{ + int ret; + uint8_t request[256]; + + (void) arg; + + /* For a cell to be considered legacy, according to the specification, the + * first 20 bytes MUST BE non-zero else it's a v3 cell. */ + memset(request, 'a', DIGEST_LEN); + memset(request + DIGEST_LEN, 0, sizeof(request) - DIGEST_LEN); + ret = introduce1_cell_is_legacy(request); + tt_int_op(ret, OP_EQ, 1); + + /* This is a NON legacy cell. */ + memset(request, 0, DIGEST_LEN); + memset(request + DIGEST_LEN, 'a', sizeof(request) - DIGEST_LEN); + ret = introduce1_cell_is_legacy(request); + tt_int_op(ret, OP_EQ, 0); + + done: + ; +} + +static void +test_introduce1_validation(void *arg) +{ + int ret; + hs_cell_introduce1_t *cell = NULL; + + (void) arg; + + /* Create our decoy cell that we'll modify as we go to test the validation + * function of that parsed cell. */ + cell = helper_create_introduce1_cell(); + + /* It should NOT be a legacy cell which will trigger a BUG(). */ + memset(cell->legacy_key_id, 'a', sizeof(cell->legacy_key_id)); + tor_capture_bugs_(1); + ret = validate_introduce1_parsed_cell(cell); + tor_end_capture_bugs_(); + tt_int_op(ret, OP_EQ, -1); + /* Reset legacy ID and make sure it's correct. */ + memset(cell->legacy_key_id, 0, sizeof(cell->legacy_key_id)); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Non existing auth key type. */ + cell->auth_key_type = 42; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset is to correct value and make sure it's correct. */ + cell->auth_key_type = HS_INTRO_AUTH_KEY_TYPE_ED25519; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Really bad key length. */ + cell->auth_key_len = 0; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + cell->auth_key_len = UINT16_MAX; + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Correct size, let's try that. */ + cell->auth_key_len = sizeof(ed25519_public_key_t); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + /* Set an invalid size of the auth key buffer. */ + hs_cell_introduce1_setlen_auth_key(cell, 3); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset auth key buffer and make sure it works. */ + hs_cell_introduce1_setlen_auth_key(cell, sizeof(ed25519_public_key_t)); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + /* Empty encrypted section. */ + hs_cell_introduce1_setlen_encrypted(cell, 0); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, -1); + /* Reset it to some non zero bytes and validate. */ + hs_cell_introduce1_setlen_encrypted(cell, 1); + ret = validate_introduce1_parsed_cell(cell); + tt_int_op(ret, OP_EQ, 0); + + done: + hs_cell_introduce1_free(cell); +} + +static void +test_received_introduce1_handling(void *arg) +{ + int ret; + uint8_t *request = NULL, buf[128]; + hs_cell_introduce1_t *cell = NULL; + or_circuit_t *circ = NULL; + + (void) arg; + + MOCK(relay_send_command_from_edge_, mock_relay_send_command_from_edge); + + hs_circuitmap_init(); + + /* Too small request length. An INTRODUCE1 expect at the very least a + * DIGEST_LEN size. */ + { + circ = helper_create_intro_circuit(); + ret = hs_intro_received_introduce1(circ, buf, DIGEST_LEN - 1); + tt_int_op(ret, OP_EQ, -1); + circuit_free(TO_CIRCUIT(circ)); + } + + /* We have a unit test only for the suitability of a circuit to receive an + * INTRODUCE1 cell so from now on we'll only test the handling of a cell. */ + + /* Bad request. */ + { + circ = helper_create_intro_circuit(); + uint8_t test[2]; /* Too small request. */ + ret = handle_introduce1(circ, test, sizeof(test)); + tor_free(circ->p_chan); + circuit_free(TO_CIRCUIT(circ)); + tt_int_op(ret, OP_EQ, -1); + } + + /* Valid case. */ + { + cell = helper_create_introduce1_cell(); + ssize_t request_len = hs_cell_introduce1_encoded_len(cell); + tt_size_op(request_len, OP_GT, 0); + request = tor_malloc_zero(request_len); + ssize_t encoded_len = + hs_cell_introduce1_encode(request, request_len, cell); + tt_size_op(encoded_len, OP_GT, 0); + + circ = helper_create_intro_circuit(); + or_circuit_t *service_circ = helper_create_intro_circuit(); + circuit_change_purpose(TO_CIRCUIT(service_circ), + CIRCUIT_PURPOSE_INTRO_POINT); + /* Register the circuit in the map for the auth key of the cell. */ + ed25519_public_key_t auth_key; + const uint8_t *cell_auth_key = + hs_cell_introduce1_getconstarray_auth_key(cell); + memcpy(auth_key.pubkey, cell_auth_key, ED25519_PUBKEY_LEN); + hs_circuitmap_register_intro_circ_v3(service_circ, &auth_key); + ret = hs_intro_received_introduce1(circ, request, request_len); + circuit_free(TO_CIRCUIT(circ)); + circuit_free(TO_CIRCUIT(service_circ)); + tt_int_op(ret, OP_EQ, 0); + } + + /* Valid legacy cell. */ + { + tor_free(request); + hs_cell_introduce1_free(cell); + cell = helper_create_introduce1_cell(); + uint8_t *legacy_key_id = hs_cell_introduce1_getarray_legacy_key_id(cell); + memset(legacy_key_id, 'a', DIGEST_LEN); + /* Add an arbitrary amount of data for the payload of a v2 cell. */ + size_t request_len = hs_cell_introduce1_encoded_len(cell) + 256; + tt_size_op(request_len, OP_GT, 0); + request = tor_malloc_zero(request_len + 256); + ssize_t encoded_len = + hs_cell_introduce1_encode(request, request_len, cell); + tt_size_op(encoded_len, OP_GT, 0); + + circ = helper_create_intro_circuit(); + or_circuit_t *service_circ = helper_create_intro_circuit(); + circuit_change_purpose(TO_CIRCUIT(service_circ), + CIRCUIT_PURPOSE_INTRO_POINT); + /* Register the circuit in the map for the auth key of the cell. */ + uint8_t token[REND_TOKEN_LEN]; + memcpy(token, legacy_key_id, sizeof(token)); + hs_circuitmap_register_intro_circ_v2(service_circ, token); + ret = hs_intro_received_introduce1(circ, request, request_len); + circuit_free(TO_CIRCUIT(circ)); + circuit_free(TO_CIRCUIT(service_circ)); + tt_int_op(ret, OP_EQ, 0); + } + + done: + hs_cell_introduce1_free(cell); + tor_free(request); + hs_circuitmap_free_all(); + UNMOCK(relay_send_command_from_edge_); +} + +struct testcase_t hs_intropoint_tests[] = { + { "intro_point_registration", + test_intro_point_registration, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_keytype", + test_establish_intro_wrong_keytype, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_keytype2", + test_establish_intro_wrong_keytype2, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_purpose", + test_establish_intro_wrong_purpose, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_sig", + test_establish_intro_wrong_sig, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_sig_len", + test_establish_intro_wrong_sig_len, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_auth_key_len", + test_establish_intro_wrong_auth_key_len, TT_FORK, NULL, NULL }, + + { "receive_establish_intro_wrong_mac", + test_establish_intro_wrong_mac, TT_FORK, NULL, NULL }, + + { "introduce1_suitable_circuit", + test_introduce1_suitable_circuit, TT_FORK, NULL, NULL }, + + { "introduce1_is_legacy", + test_introduce1_is_legacy, TT_FORK, NULL, NULL }, + + { "introduce1_validation", + test_introduce1_validation, TT_FORK, NULL, NULL }, + + { "received_introduce1_handling", + test_received_introduce1_handling, TT_FORK, NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_hs_service.c b/src/test/test_hs_service.c new file mode 100644 index 0000000000..039d727cea --- /dev/null +++ b/src/test/test_hs_service.c @@ -0,0 +1,111 @@ +/* Copyright (c) 2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +/** + * \file test_hs_service.c + * \brief Test hidden service functionality. + */ + +#define HS_SERVICE_PRIVATE +#define HS_INTROPOINT_PRIVATE + +#include "test.h" +#include "log_test_helpers.h" +#include "crypto.h" + +#include "hs/cell_establish_intro.h" +#include "hs_service.h" +#include "hs_intropoint.h" + +/** We simulate the creation of an outgoing ESTABLISH_INTRO cell, and then we + * parse it from the receiver side. */ +static void +test_gen_establish_intro_cell(void *arg) +{ + (void) arg; + ssize_t retval; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + uint8_t buf[RELAY_PAYLOAD_SIZE]; + hs_cell_establish_intro_t *cell_out = NULL; + hs_cell_establish_intro_t *cell_in = NULL; + + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + + /* Create outgoing ESTABLISH_INTRO cell and extract its payload so that we + attempt to parse it. */ + { + cell_out = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + tt_assert(cell_out); + + retval = get_establish_intro_payload(buf, sizeof(buf), cell_out); + tt_int_op(retval, >=, 0); + } + + /* Parse it as the receiver */ + { + ssize_t parse_result = hs_cell_establish_intro_parse(&cell_in, + buf, sizeof(buf)); + tt_int_op(parse_result, >=, 0); + + retval = verify_establish_intro_cell(cell_in, + circuit_key_material, + sizeof(circuit_key_material)); + tt_int_op(retval, >=, 0); + } + + done: + hs_cell_establish_intro_free(cell_out); + hs_cell_establish_intro_free(cell_in); +} + +/* Mocked ed25519_sign_prefixed() function that always fails :) */ +static int +mock_ed25519_sign_prefixed(ed25519_signature_t *signature_out, + const uint8_t *msg, size_t msg_len, + const char *prefix_str, + const ed25519_keypair_t *keypair) { + (void) signature_out; + (void) msg; + (void) msg_len; + (void) prefix_str; + (void) keypair; + return -1; +} + +/** We simulate a failure to create an ESTABLISH_INTRO cell */ +static void +test_gen_establish_intro_cell_bad(void *arg) +{ + (void) arg; + hs_cell_establish_intro_t *cell = NULL; + uint8_t circuit_key_material[DIGEST_LEN] = {0}; + + MOCK(ed25519_sign_prefixed, mock_ed25519_sign_prefixed); + + crypto_rand((char *) circuit_key_material, sizeof(circuit_key_material)); + + setup_full_capture_of_logs(LOG_WARN); + /* Easiest way to make that function fail is to mock the + ed25519_sign_prefixed() function and make it fail. */ + cell = generate_establish_intro_cell(circuit_key_material, + sizeof(circuit_key_material)); + expect_log_msg_containing("Unable to gen signature for " + "ESTABLISH_INTRO cell."); + teardown_capture_of_logs(); + tt_assert(!cell); + + done: + hs_cell_establish_intro_free(cell); + UNMOCK(ed25519_sign_prefixed); +} + +struct testcase_t hs_service_tests[] = { + { "gen_establish_intro_cell", test_gen_establish_intro_cell, TT_FORK, + NULL, NULL }, + { "gen_establish_intro_cell_bad", test_gen_establish_intro_cell_bad, TT_FORK, + NULL, NULL }, + + END_OF_TESTCASES +}; + diff --git a/src/test/test_link_handshake.c b/src/test/test_link_handshake.c index ddf66f4d34..d81f9e8eb2 100644 --- a/src/test/test_link_handshake.c +++ b/src/test/test_link_handshake.c @@ -6,13 +6,27 @@ #define CHANNELTLS_PRIVATE #define CONNECTION_PRIVATE #define TOR_CHANNEL_INTERNAL_ +#define TORTLS_PRIVATE + +#include "compat.h" + +/* Some versions of OpenSSL declare SSL_get_selected_srtp_profile twice in + * srtp.h. Suppress the GCC warning so we can build with -Wredundant-decl. */ +DISABLE_GCC_WARNING(redundant-decls) +#include <openssl/x509.h> +#include <openssl/ssl.h> +ENABLE_GCC_WARNING(redundant-decls) + #include "or.h" #include "config.h" #include "connection.h" #include "connection_or.h" #include "channeltls.h" #include "link_handshake.h" +#include "router.h" +#include "routerkeys.h" #include "scheduler.h" +#include "torcert.h" #include "test.h" #include "log_test_helpers.h" @@ -37,6 +51,16 @@ mock_tls_cert_matches_key(const tor_tls_t *tls, const tor_x509_cert_t *cert) (void) cert; // XXXX look at this. return 1; } +static tor_tls_t *mock_peer_cert_expect_tortls = NULL; +static tor_x509_cert_t *mock_peer_cert = NULL; +static tor_x509_cert_t * +mock_get_peer_cert(tor_tls_t *tls) +{ + if (mock_peer_cert_expect_tortls && + mock_peer_cert_expect_tortls != tls) + return NULL; + return tor_x509_cert_dup(mock_peer_cert); +} static int mock_send_netinfo_called = 0; static int @@ -57,14 +81,29 @@ mock_close_for_err(or_connection_t *orconn, int flush) } static int mock_send_authenticate_called = 0; +static int mock_send_authenticate_called_with_type = 0; static int mock_send_authenticate(or_connection_t *conn, int type) { (void) conn; - (void) type; + mock_send_authenticate_called_with_type = type; ++mock_send_authenticate_called;// XXX check_this return 0; } +static int +mock_export_key_material(tor_tls_t *tls, uint8_t *secrets_out, + const uint8_t *context, + size_t context_len, + const char *label) +{ + (void) tls; + (void)secrets_out; + (void)context; + (void)context_len; + (void)label; + memcpy(secrets_out, "int getRandomNumber(){return 4;}", 32); + return 0; +} static tor_x509_cert_t *mock_own_cert = NULL; static tor_x509_cert_t * @@ -78,20 +117,23 @@ mock_get_own_cert(tor_tls_t *tls) static void test_link_handshake_certs_ok(void *arg) { - (void) arg; - or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); or_connection_t *c2 = or_connection_new(CONN_TYPE_OR, AF_INET); var_cell_t *cell1 = NULL, *cell2 = NULL; certs_cell_t *cc1 = NULL, *cc2 = NULL; channel_tls_t *chan1 = NULL, *chan2 = NULL; crypto_pk_t *key1 = NULL, *key2 = NULL; + const int with_ed = !strcmp((const char *)arg, "Ed25519"); + + tor_addr_from_ipv4h(&c1->base_.addr, 0x7f000001); + tor_addr_from_ipv4h(&c2->base_.addr, 0x7f000001); scheduler_init(); MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); MOCK(connection_or_send_netinfo, mock_send_netinfo); + MOCK(tor_tls_get_peer_cert, mock_get_peer_cert); MOCK(tor_tls_get_own_cert, mock_get_own_cert); key1 = pk_generate(2); @@ -103,6 +145,15 @@ test_link_handshake_certs_ok(void *arg) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, key1, key2, 86400), ==, 0); + if (with_ed) { + /* If we're making a CERTS cell for an ed handshake, let's make sure we + * have some Ed25519 certificates and keys. */ + init_mock_ed_keys(key2); + } else { + certs_cell_ed25519_disabled_for_testing = 1; + } + + /* c1 has started_here == 1 */ { const tor_x509_cert_t *link_cert = NULL; tt_assert(!tor_tls_get_my_certs(1, &link_cert, NULL)); @@ -113,6 +164,7 @@ test_link_handshake_certs_ok(void *arg) c1->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c1, 1), ==, 0); + /* c2 has started_here == 0 */ c2->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; c2->link_proto = 3; tt_int_op(connection_init_or_handshake_state(c2, 0), ==, 0); @@ -136,8 +188,13 @@ test_link_handshake_certs_ok(void *arg) tt_int_op(cell2->payload_len, ==, certs_cell_parse(&cc2, cell2->payload, cell2->payload_len)); - tt_int_op(2, ==, cc1->n_certs); - tt_int_op(2, ==, cc2->n_certs); + if (with_ed) { + tt_int_op(5, ==, cc1->n_certs); + tt_int_op(5, ==, cc2->n_certs); + } else { + tt_int_op(2, ==, cc1->n_certs); + tt_int_op(2, ==, cc2->n_certs); + } tt_int_op(certs_cell_get_certs(cc1, 0)->cert_type, ==, CERTTYPE_RSA1024_ID_AUTH); @@ -149,6 +206,22 @@ test_link_handshake_certs_ok(void *arg) tt_int_op(certs_cell_get_certs(cc2, 1)->cert_type, ==, CERTTYPE_RSA1024_ID_ID); + if (with_ed) { + tt_int_op(certs_cell_get_certs(cc1, 2)->cert_type, ==, + CERTTYPE_ED_ID_SIGN); + tt_int_op(certs_cell_get_certs(cc1, 3)->cert_type, ==, + CERTTYPE_ED_SIGN_AUTH); + tt_int_op(certs_cell_get_certs(cc1, 4)->cert_type, ==, + CERTTYPE_RSA1024_ID_EDID); + + tt_int_op(certs_cell_get_certs(cc2, 2)->cert_type, ==, + CERTTYPE_ED_ID_SIGN); + tt_int_op(certs_cell_get_certs(cc2, 3)->cert_type, ==, + CERTTYPE_ED_SIGN_LINK); + tt_int_op(certs_cell_get_certs(cc2, 4)->cert_type, ==, + CERTTYPE_RSA1024_ID_EDID); + } + chan1 = tor_malloc_zero(sizeof(*chan1)); channel_tls_common_init(chan1); c1->chan = chan1; @@ -159,13 +232,38 @@ test_link_handshake_certs_ok(void *arg) c1->base_.conn_array_index = -1; crypto_pk_get_digest(key2, c1->identity_digest); + if (with_ed) { + const tor_x509_cert_t *linkc, *idc; + tor_tls_get_my_certs(1, &linkc, &idc); + mock_peer_cert_expect_tortls = c1->tls; /* We should see this tls... */ + mock_peer_cert = tor_x509_cert_dup(linkc); /* and when we do, the peer's + * cert is this... */ + } channel_tls_process_certs_cell(cell2, chan1); + mock_peer_cert_expect_tortls = NULL; + mock_peer_cert = NULL; + + tor_assert(c1->handshake_state->authenticated); tt_assert(c1->handshake_state->received_certs_cell); - tt_assert(c1->handshake_state->auth_cert == NULL); - tt_assert(c1->handshake_state->id_cert); + tt_assert(c1->handshake_state->certs->auth_cert == NULL); + tt_assert(c1->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(c1->handshake_state->certs->id_cert); + if (with_ed) { + tt_assert(c1->handshake_state->certs->ed_sign_link); + tt_assert(c1->handshake_state->certs->ed_rsa_crosscert); + tt_assert(c1->handshake_state->certs->ed_id_sign); + tt_assert(c1->handshake_state->authenticated_rsa); + tt_assert(c1->handshake_state->authenticated_ed25519); + } else { + tt_assert(c1->handshake_state->certs->ed_sign_link == NULL); + tt_assert(c1->handshake_state->certs->ed_rsa_crosscert == NULL); + tt_assert(c1->handshake_state->certs->ed_id_sign == NULL); + tt_assert(c1->handshake_state->authenticated_rsa); + tt_assert(! c1->handshake_state->authenticated_ed25519); + } tt_assert(! tor_mem_is_zero( - (char*)c1->handshake_state->authenticated_peer_id, 20)); + (char*)c1->handshake_state->authenticated_rsa_peer_id, 20)); chan2 = tor_malloc_zero(sizeof(*chan2)); channel_tls_common_init(chan2); @@ -180,15 +278,30 @@ test_link_handshake_certs_ok(void *arg) channel_tls_process_certs_cell(cell1, chan2); tt_assert(c2->handshake_state->received_certs_cell); - tt_assert(c2->handshake_state->auth_cert); - tt_assert(c2->handshake_state->id_cert); + if (with_ed) { + tt_assert(c2->handshake_state->certs->ed_sign_auth); + tt_assert(c2->handshake_state->certs->ed_rsa_crosscert); + tt_assert(c2->handshake_state->certs->ed_id_sign); + } else { + tt_assert(c2->handshake_state->certs->auth_cert); + tt_assert(c2->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(c2->handshake_state->certs->ed_rsa_crosscert == NULL); + tt_assert(c2->handshake_state->certs->ed_id_sign == NULL); + } + tt_assert(c2->handshake_state->certs->id_cert); tt_assert(tor_mem_is_zero( - (char*)c2->handshake_state->authenticated_peer_id, 20)); + (char*)c2->handshake_state->authenticated_rsa_peer_id, 20)); + /* no authentication has happened yet, since we haen't gotten an AUTH cell. + */ + tt_assert(! c2->handshake_state->authenticated); + tt_assert(! c2->handshake_state->authenticated_rsa); + tt_assert(! c2->handshake_state->authenticated_ed25519); done: UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_write_var_cell_to_buf); UNMOCK(connection_or_send_netinfo); + UNMOCK(tor_tls_get_peer_cert); UNMOCK(tor_tls_get_own_cert); tor_x509_cert_free(mock_own_cert); mock_own_cert = NULL; @@ -211,6 +324,8 @@ test_link_handshake_certs_ok(void *arg) } typedef struct certs_data_s { + int is_ed; + int is_link_cert; or_connection_t *c; channel_tls_t *chan; certs_cell_t *ccell; @@ -226,11 +341,13 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) UNMOCK(tor_tls_cert_matches_key); UNMOCK(connection_or_send_netinfo); UNMOCK(connection_or_close_for_error); + UNMOCK(tor_tls_get_peer_cert); + UNMOCK(tor_tls_get_own_cert); if (d) { tor_free(d->cell); certs_cell_free(d->ccell); - connection_or_remove_from_identity_map(d->c); + connection_or_clear_identity(d->c); connection_free_(TO_CONN(d->c)); circuitmux_free(d->chan->base_.cmux); tor_free(d->chan); @@ -238,6 +355,7 @@ recv_certs_cleanup(const struct testcase_t *test, void *obj) crypto_pk_free(d->key2); tor_free(d); } + routerkeys_free_all(); return 1; } @@ -249,11 +367,18 @@ recv_certs_setup(const struct testcase_t *test) certs_cell_cert_t *ccc1 = NULL; certs_cell_cert_t *ccc2 = NULL; ssize_t n; + int is_ed = d->is_ed = !strcmpstart(test->setup_data, "Ed25519"); + int is_rsa = !strcmpstart(test->setup_data, "RSA"); + int is_link = d->is_link_cert = !strcmpend(test->setup_data, "-Link"); + int is_auth = !strcmpend(test->setup_data, "-Auth"); + tor_assert(is_ed != is_rsa); + tor_assert(is_link != is_auth); d->c = or_connection_new(CONN_TYPE_OR, AF_INET); d->chan = tor_malloc_zero(sizeof(*d->chan)); d->c->chan = d->chan; d->c->base_.address = tor_strdup("HaveAnAddress"); + tor_addr_from_ipv4h(&d->c->base_.addr, 0x801f0127); d->c->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; d->chan->conn = d->c; tt_int_op(connection_init_or_handshake_state(d->c, 1), ==, 0); @@ -264,19 +389,25 @@ recv_certs_setup(const struct testcase_t *test) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, d->key1, d->key2, 86400), ==, 0); + if (is_ed) { + init_mock_ed_keys(d->key2); + } else { + routerkeys_free_all(); + } + d->ccell = certs_cell_new(); ccc1 = certs_cell_cert_new(); certs_cell_add_certs(d->ccell, ccc1); ccc2 = certs_cell_cert_new(); certs_cell_add_certs(d->ccell, ccc2); d->ccell->n_certs = 2; - ccc1->cert_type = 1; + ccc1->cert_type = is_link ? 1 : 3; ccc2->cert_type = 2; const tor_x509_cert_t *a,*b; const uint8_t *enca, *encb; size_t lena, lenb; - tor_tls_get_my_certs(1, &a, &b); + tor_tls_get_my_certs(is_link ? 1 : 0, &a, &b); tor_x509_cert_get_der(a, &enca, &lena); tor_x509_cert_get_der(b, &encb, &lenb); certs_cell_cert_setlen_body(ccc1, lena); @@ -287,6 +418,41 @@ recv_certs_setup(const struct testcase_t *test) memcpy(certs_cell_cert_getarray_body(ccc1), enca, lena); memcpy(certs_cell_cert_getarray_body(ccc2), encb, lenb); + if (is_ed) { + certs_cell_cert_t *ccc3 = NULL; /* Id->Sign */ + certs_cell_cert_t *ccc4 = NULL; /* Sign->Link or Sign->Auth. */ + certs_cell_cert_t *ccc5 = NULL; /* RSAId->Ed Id. */ + const tor_cert_t *id_sign = get_master_signing_key_cert(); + const tor_cert_t *secondary = + is_link ? get_current_link_cert_cert() : get_current_auth_key_cert(); + const uint8_t *cc = NULL; + size_t cc_sz; + get_master_rsa_crosscert(&cc, &cc_sz); + + ccc3 = certs_cell_cert_new(); + ccc4 = certs_cell_cert_new(); + ccc5 = certs_cell_cert_new(); + certs_cell_add_certs(d->ccell, ccc3); + certs_cell_add_certs(d->ccell, ccc4); + certs_cell_add_certs(d->ccell, ccc5); + ccc3->cert_len = id_sign->encoded_len; + ccc4->cert_len = secondary->encoded_len; + ccc5->cert_len = cc_sz; + certs_cell_cert_setlen_body(ccc3, ccc3->cert_len); + certs_cell_cert_setlen_body(ccc4, ccc4->cert_len); + certs_cell_cert_setlen_body(ccc5, ccc5->cert_len); + memcpy(certs_cell_cert_getarray_body(ccc3), id_sign->encoded, + ccc3->cert_len); + memcpy(certs_cell_cert_getarray_body(ccc4), secondary->encoded, + ccc4->cert_len); + memcpy(certs_cell_cert_getarray_body(ccc5), cc, ccc5->cert_len); + ccc3->cert_type = 4; + ccc4->cert_type = is_link ? 5 : 6; + ccc5->cert_type = 7; + + d->ccell->n_certs = 5; + } + d->cell = var_cell_new(4096); d->cell->command = CELL_CERTS; @@ -297,6 +463,12 @@ recv_certs_setup(const struct testcase_t *test) MOCK(tor_tls_cert_matches_key, mock_tls_cert_matches_key); MOCK(connection_or_send_netinfo, mock_send_netinfo); MOCK(connection_or_close_for_error, mock_close_for_err); + MOCK(tor_tls_get_peer_cert, mock_get_peer_cert); + + if (is_link) { + /* Say that this is the peer's certificate */ + mock_peer_cert = tor_x509_cert_dup(a); + } tt_int_op(0, ==, d->c->handshake_state->received_certs_cell); tt_int_op(0, ==, mock_send_authenticate_called); @@ -320,9 +492,24 @@ test_link_handshake_recv_certs_ok(void *arg) channel_tls_process_certs_cell(d->cell, d->chan); tt_int_op(0, ==, mock_close_called); tt_int_op(d->c->handshake_state->authenticated, ==, 1); + tt_int_op(d->c->handshake_state->authenticated_rsa, ==, 1); tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1); - tt_assert(d->c->handshake_state->id_cert != NULL); - tt_assert(d->c->handshake_state->auth_cert == NULL); + tt_assert(d->c->handshake_state->certs->id_cert != NULL); + tt_assert(d->c->handshake_state->certs->auth_cert == NULL); + + if (d->is_ed) { + tt_assert(d->c->handshake_state->certs->ed_id_sign != NULL); + tt_assert(d->c->handshake_state->certs->ed_sign_link != NULL); + tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(d->c->handshake_state->certs->ed_rsa_crosscert != NULL); + tt_int_op(d->c->handshake_state->authenticated_ed25519, ==, 1); + } else { + tt_assert(d->c->handshake_state->certs->ed_id_sign == NULL); + tt_assert(d->c->handshake_state->certs->ed_sign_link == NULL); + tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(d->c->handshake_state->certs->ed_rsa_crosscert == NULL); + tt_int_op(d->c->handshake_state->authenticated_ed25519, ==, 0); + } done: ; @@ -333,17 +520,20 @@ test_link_handshake_recv_certs_ok_server(void *arg) { certs_data_t *d = arg; d->c->handshake_state->started_here = 0; - certs_cell_get_certs(d->ccell, 0)->cert_type = 3; - certs_cell_get_certs(d->ccell, 1)->cert_type = 2; - ssize_t n = certs_cell_encode(d->cell->payload, 2048, d->ccell); - tt_int_op(n, >, 0); - d->cell->payload_len = n; + d->c->handshake_state->certs->started_here = 0; channel_tls_process_certs_cell(d->cell, d->chan); tt_int_op(0, ==, mock_close_called); tt_int_op(d->c->handshake_state->authenticated, ==, 0); tt_int_op(d->c->handshake_state->received_certs_cell, ==, 1); - tt_assert(d->c->handshake_state->id_cert != NULL); - tt_assert(d->c->handshake_state->auth_cert != NULL); + tt_assert(d->c->handshake_state->certs->id_cert != NULL); + tt_assert(d->c->handshake_state->certs->link_cert == NULL); + if (d->is_ed) { + tt_assert(d->c->handshake_state->certs->ed_sign_auth != NULL); + tt_assert(d->c->handshake_state->certs->auth_cert == NULL); + } else { + tt_assert(d->c->handshake_state->certs->ed_sign_auth == NULL); + tt_assert(d->c->handshake_state->certs->auth_cert != NULL); + } done: ; @@ -361,6 +551,8 @@ test_link_handshake_recv_certs_ok_server(void *arg) tt_int_op(1, ==, mock_close_called); \ tt_int_op(0, ==, mock_send_authenticate_called); \ tt_int_op(0, ==, mock_send_netinfo_called); \ + tt_int_op(0, ==, d->c->handshake_state->authenticated_rsa); \ + tt_int_op(0, ==, d->c->handshake_state->authenticated_ed25519); \ if (require_failure_message) { \ expect_log_msg_containing(require_failure_message); \ } \ @@ -401,12 +593,41 @@ CERTS_FAIL(truncated_3, d->cell->payload_len = 7; memcpy(d->cell->payload, "\x01\x01\x00\x05""abc", 7); }) +CERTS_FAIL(truncated_4, /* ed25519 */ + { + require_failure_message = "It couldn't be parsed"; + d->cell->payload_len -= 10; + }) +CERTS_FAIL(truncated_5, /* ed25519 */ + { + require_failure_message = "It couldn't be parsed"; + d->cell->payload_len -= 100; + }) + #define REENCODE() do { \ + const char *msg = certs_cell_check(d->ccell); \ + if (msg) puts(msg); \ ssize_t n = certs_cell_encode(d->cell->payload, 4096, d->ccell); \ tt_int_op(n, >, 0); \ d->cell->payload_len = n; \ } while (0) +CERTS_FAIL(truncated_6, /* ed25519 */ + { + /* truncate the link certificate */ + require_failure_message = "undecodable Ed certificate"; + certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 3), 7); + certs_cell_get_certs(d->ccell, 3)->cert_len = 7; + REENCODE(); + }) +CERTS_FAIL(truncated_7, /* ed25519 */ + { + /* truncate the crosscert */ + require_failure_message = "Unparseable or overlong crosscert"; + certs_cell_cert_setlen_body(certs_cell_get_certs(d->ccell, 4), 7); + certs_cell_get_certs(d->ccell, 4)->cert_len = 7; + REENCODE(); + }) CERTS_FAIL(not_x509, { require_failure_message = "Received undecodable certificate"; @@ -435,6 +656,206 @@ CERTS_FAIL(both_auth, certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); }) +CERTS_FAIL(duplicate_id, /* ed25519 */ + { + require_failure_message = "Duplicate Ed25519 certificate"; + certs_cell_get_certs(d->ccell, 2)->cert_type = 4; + certs_cell_get_certs(d->ccell, 3)->cert_type = 4; + REENCODE(); + }) +CERTS_FAIL(duplicate_link, /* ed25519 */ + { + require_failure_message = "Duplicate Ed25519 certificate"; + certs_cell_get_certs(d->ccell, 2)->cert_type = 5; + certs_cell_get_certs(d->ccell, 3)->cert_type = 5; + REENCODE(); + }) +CERTS_FAIL(duplicate_crosscert, /* ed25519 */ + { + require_failure_message = "Duplicate RSA->Ed25519 crosscert"; + certs_cell_get_certs(d->ccell, 2)->cert_type = 7; + certs_cell_get_certs(d->ccell, 3)->cert_type = 7; + REENCODE(); + }) +static void +test_link_handshake_recv_certs_missing_id(void *arg) /* ed25519 */ +{ + certs_data_t *d = arg; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_set_certs(d->ccell, 2, certs_cell_get_certs(d->ccell, 4)); + certs_cell_set0_certs(d->ccell, 4, NULL); /* prevent free */ + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + + /* This handshake succeeds, but since we have no ID cert, we will + * just do the RSA handshake. */ + channel_tls_process_certs_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(0, ==, d->c->handshake_state->authenticated_ed25519); + tt_int_op(1, ==, d->c->handshake_state->authenticated_rsa); + done: + ; +} +CERTS_FAIL(missing_signing_key, /* ed25519 */ + { + require_failure_message = "No Ed25519 signing key"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 2); + tt_int_op(cert->cert_type, ==, CERTTYPE_ED_ID_SIGN); + /* replace this with a valid master->signing cert, but with no + * signing key. */ + const ed25519_keypair_t *mk = get_master_identity_keypair(); + const ed25519_keypair_t *sk = get_master_signing_keypair(); + tor_cert_t *bad_cert = tor_cert_create(mk, CERT_TYPE_ID_SIGNING, + &sk->pubkey, time(NULL), 86400, + 0 /* don't include signer */); + certs_cell_cert_setlen_body(cert, bad_cert->encoded_len); + memcpy(certs_cell_cert_getarray_body(cert), + bad_cert->encoded, bad_cert->encoded_len); + cert->cert_len = bad_cert->encoded_len; + tor_cert_free(bad_cert); + REENCODE(); + }) +CERTS_FAIL(missing_link, /* ed25519 */ + { + require_failure_message = "No Ed25519 link key"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_set_certs(d->ccell, 3, certs_cell_get_certs(d->ccell, 4)); + certs_cell_set0_certs(d->ccell, 4, NULL); /* prevent free */ + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + }) +CERTS_FAIL(missing_auth, /* ed25519 */ + { + d->c->handshake_state->started_here = 0; + d->c->handshake_state->certs->started_here = 0; + require_failure_message = "No Ed25519 link authentication key"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_set_certs(d->ccell, 3, certs_cell_get_certs(d->ccell, 4)); + certs_cell_set0_certs(d->ccell, 4, NULL); /* prevent free */ + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + }) +CERTS_FAIL(missing_crosscert, /* ed25519 */ + { + require_failure_message = "Missing RSA->Ed25519 crosscert"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + }) +CERTS_FAIL(missing_rsa_id, /* ed25519 */ + { + require_failure_message = "Missing legacy RSA ID cert"; + tt_int_op(certs_cell_getlen_certs(d->ccell), OP_EQ, 5); + certs_cell_set_certs(d->ccell, 1, certs_cell_get_certs(d->ccell, 4)); + certs_cell_set0_certs(d->ccell, 4, NULL); /* prevent free */ + certs_cell_setlen_certs(d->ccell, 4); + d->ccell->n_certs = 4; + REENCODE(); + }) +CERTS_FAIL(link_mismatch, /* ed25519 */ + { + require_failure_message = "Link certificate does not match " + "TLS certificate"; + const tor_x509_cert_t *idc; + tor_tls_get_my_certs(1, NULL, &idc); + tor_x509_cert_free(mock_peer_cert); + /* Pretend that the peer cert was something else. */ + mock_peer_cert = tor_x509_cert_dup(idc); + /* No reencode needed. */ + }) +CERTS_FAIL(bad_ed_sig, /* ed25519 */ + { + require_failure_message = "At least one Ed25519 certificate was " + "badly signed"; + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 3); + uint8_t *body = certs_cell_cert_getarray_body(cert); + ssize_t body_len = certs_cell_cert_getlen_body(cert); + /* Frob a byte in the signature */ + body[body_len - 13] ^= 7; + REENCODE(); + }) +CERTS_FAIL(bad_crosscert, /*ed25519*/ + { + require_failure_message = "Invalid RSA->Ed25519 crosscert"; + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 4); + uint8_t *body = certs_cell_cert_getarray_body(cert); + ssize_t body_len = certs_cell_cert_getlen_body(cert); + /* Frob a byte in the signature */ + body[body_len - 13] ^= 7; + REENCODE(); + }) +CERTS_FAIL(bad_rsa_id_cert, /*ed25519*/ + { + require_failure_message = "legacy RSA ID certificate was not valid"; + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1); + uint8_t *body = certs_cell_cert_getarray_body(cert); + ssize_t body_len = certs_cell_cert_getlen_body(cert); + /* Frob a byte in the signature */ + body[body_len - 13] ^= 7; + REENCODE(); + }) +CERTS_FAIL(expired_rsa_id, /* both */ + { + require_failure_message = "Certificate already expired"; + /* we're going to replace the identity cert with an expired one. */ + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 1); + const tor_x509_cert_t *idc; + tor_tls_get_my_certs(1, NULL, &idc); + X509 *newc = X509_dup(idc->cert); + time_t new_end = time(NULL) - 86400 * 10; + X509_time_adj(X509_get_notAfter(newc), 0, &new_end); + EVP_PKEY *pk = crypto_pk_get_evp_pkey_(d->key2, 1); + tt_assert(X509_sign(newc, pk, EVP_sha1())); + int len = i2d_X509(newc, NULL); + certs_cell_cert_setlen_body(cert, len); + uint8_t *body = certs_cell_cert_getarray_body(cert); + int len2 = i2d_X509(newc, &body); + tt_int_op(len, ==, len2); + REENCODE(); + X509_free(newc); + EVP_PKEY_free(pk); + }) +CERTS_FAIL(expired_ed_id, /* ed25519 */ + { + /* we're going to replace the Ed Id->sign cert with an expired one. */ + require_failure_message = "At least one certificate expired"; + /* We don't need to re-sign, since we check for expiration first. */ + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 2); + uint8_t *body = certs_cell_cert_getarray_body(cert); + /* The expiration field is bytes [2..5]. It is in HOURS since the + * epoch. */ + set_uint32(body+2, htonl(24)); /* Back to jan 2, 1970. */ + REENCODE(); + }) +CERTS_FAIL(expired_ed_link, /* ed25519 */ + { + /* we're going to replace the Ed Sign->link cert with an expired one. */ + require_failure_message = "At least one certificate expired"; + /* We don't need to re-sign, since we check for expiration first. */ + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 3); + uint8_t *body = certs_cell_cert_getarray_body(cert); + /* The expiration field is bytes [2..5]. It is in HOURS since the + * epoch. */ + set_uint32(body+2, htonl(24)); /* Back to jan 2, 1970. */ + REENCODE(); + }) +CERTS_FAIL(expired_crosscert, /* ed25519 */ + { + /* we're going to replace the Ed Sign->link cert with an expired one. */ + require_failure_message = "Crosscert is expired"; + /* We don't need to re-sign, since we check for expiration first. */ + certs_cell_cert_t *cert = certs_cell_get_certs(d->ccell, 4); + uint8_t *body = certs_cell_cert_getarray_body(cert); + /* The expiration field is bytes [32..35]. once again, HOURS. */ + set_uint32(body+32, htonl(24)); /* Back to jan 2, 1970. */ + REENCODE(); + }) + CERTS_FAIL(wrong_labels_1, { require_failure_message = "The link certificate was not valid"; @@ -459,21 +880,26 @@ CERTS_FAIL(wrong_labels_2, }) CERTS_FAIL(wrong_labels_3, { - require_failure_message = "The certs we wanted were missing"; + require_failure_message = + "The certs we wanted (ID, Link) were missing"; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); }) CERTS_FAIL(server_missing_certs, { - require_failure_message = "The certs we wanted were missing"; + require_failure_message = + "The certs we wanted (ID, Auth) were missing"; d->c->handshake_state->started_here = 0; + d->c->handshake_state->certs->started_here = 0; + }) CERTS_FAIL(server_wrong_labels_1, { require_failure_message = "The authentication certificate was not valid"; d->c->handshake_state->started_here = 0; + d->c->handshake_state->certs->started_here = 0; certs_cell_get_certs(d->ccell, 0)->cert_type = 2; certs_cell_get_certs(d->ccell, 1)->cert_type = 3; REENCODE(); @@ -487,6 +913,11 @@ test_link_handshake_send_authchallenge(void *arg) or_connection_t *c1 = or_connection_new(CONN_TYPE_OR, AF_INET); var_cell_t *cell1=NULL, *cell2=NULL; + crypto_pk_t *rsa0 = pk_generate(0), *rsa1 = pk_generate(1); + tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, + rsa0, rsa1, 86400), ==, 0); + init_mock_ed_keys(rsa0); + MOCK(connection_or_write_var_cell_to_buf, mock_write_var_cell); tt_int_op(connection_init_or_handshake_state(c1, 0), ==, 0); @@ -496,15 +927,15 @@ test_link_handshake_send_authchallenge(void *arg) cell1 = mock_got_var_cell; tt_int_op(0, ==, connection_or_send_auth_challenge_cell(c1)); cell2 = mock_got_var_cell; - tt_int_op(36, ==, cell1->payload_len); - tt_int_op(36, ==, cell2->payload_len); + tt_int_op(38, ==, cell1->payload_len); + tt_int_op(38, ==, cell2->payload_len); tt_int_op(0, ==, cell1->circ_id); tt_int_op(0, ==, cell2->circ_id); tt_int_op(CELL_AUTH_CHALLENGE, ==, cell1->command); tt_int_op(CELL_AUTH_CHALLENGE, ==, cell2->command); - tt_mem_op("\x00\x01\x00\x01", ==, cell1->payload + 32, 4); - tt_mem_op("\x00\x01\x00\x01", ==, cell2->payload + 32, 4); + tt_mem_op("\x00\x02\x00\x01\x00\x03", ==, cell1->payload + 32, 6); + tt_mem_op("\x00\x02\x00\x01\x00\x03", ==, cell2->payload + 32, 6); tt_mem_op(cell1->payload, !=, cell2->payload, 32); done: @@ -512,6 +943,8 @@ test_link_handshake_send_authchallenge(void *arg) connection_free_(TO_CONN(c1)); tor_free(cell1); tor_free(cell2); + crypto_pk_free(rsa0); + crypto_pk_free(rsa1); } typedef struct authchallenge_data_s { @@ -556,9 +989,9 @@ recv_authchallenge_setup(const struct testcase_t *test) d->c->handshake_state->received_certs_cell = 1; d->cell = var_cell_new(128); d->cell->payload_len = 38; - d->cell->payload[33] = 2; - d->cell->payload[35] = 7; - d->cell->payload[37] = 1; + d->cell->payload[33] = 2; /* 2 methods */ + d->cell->payload[35] = 7; /* This one isn't real */ + d->cell->payload[37] = 1; /* This is the old RSA one. */ d->cell->command = CELL_AUTH_CHALLENGE; get_options_mutable()->ORPort_set = 1; @@ -566,7 +999,6 @@ recv_authchallenge_setup(const struct testcase_t *test) MOCK(connection_or_close_for_error, mock_close_for_err); MOCK(connection_or_send_netinfo, mock_send_netinfo); MOCK(connection_or_send_authenticate_cell, mock_send_authenticate); - tt_int_op(0, ==, d->c->handshake_state->received_auth_challenge); tt_int_op(0, ==, mock_send_authenticate_called); tt_int_op(0, ==, mock_send_netinfo_called); @@ -592,6 +1024,26 @@ test_link_handshake_recv_authchallenge_ok(void *arg) tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); tt_int_op(1, ==, mock_send_authenticate_called); tt_int_op(1, ==, mock_send_netinfo_called); + tt_int_op(1, ==, mock_send_authenticate_called_with_type); /* RSA */ + done: + ; +} + +static void +test_link_handshake_recv_authchallenge_ok_ed25519(void *arg) +{ + authchallenge_data_t *d = arg; + + /* Add the ed25519 authentication mechanism here. */ + d->cell->payload[33] = 3; /* 3 types are supported now. */ + d->cell->payload[39] = 3; + d->cell->payload_len += 2; + channel_tls_process_auth_challenge_cell(d->cell, d->chan); + tt_int_op(0, ==, mock_close_called); + tt_int_op(1, ==, d->c->handshake_state->received_auth_challenge); + tt_int_op(1, ==, mock_send_authenticate_called); + tt_int_op(1, ==, mock_send_netinfo_called); + tt_int_op(3, ==, mock_send_authenticate_called_with_type); /* Ed25519 */ done: ; } @@ -655,7 +1107,8 @@ AUTHCHALLENGE_FAIL(badproto, AUTHCHALLENGE_FAIL(as_server, require_failure_message = "We didn't originate this " "connection"; - d->c->handshake_state->started_here = 0;) + d->c->handshake_state->started_here = 0; + d->c->handshake_state->certs->started_here = 0;) AUTHCHALLENGE_FAIL(duplicate, require_failure_message = "We already received one"; d->c->handshake_state->received_auth_challenge = 1) @@ -673,15 +1126,6 @@ AUTHCHALLENGE_FAIL(nonzero_circid, require_failure_message = "It had a nonzero circuit ID"; d->cell->circ_id = 1337) -static tor_x509_cert_t *mock_peer_cert = NULL; - -static tor_x509_cert_t * -mock_get_peer_cert(tor_tls_t *tls) -{ - (void)tls; - return tor_x509_cert_dup(mock_peer_cert); -} - static int mock_get_tlssecrets(tor_tls_t *tls, uint8_t *secrets_out) { @@ -701,6 +1145,7 @@ mock_set_circid_type(channel_t *chan, } typedef struct authenticate_data_s { + int is_ed; or_connection_t *c1, *c2; channel_tls_t *chan2; var_cell_t *cell; @@ -717,11 +1162,12 @@ authenticate_data_cleanup(const struct testcase_t *test, void *arg) UNMOCK(tor_tls_get_tlssecrets); UNMOCK(connection_or_close_for_error); UNMOCK(channel_set_circid_type); + UNMOCK(tor_tls_export_key_material); authenticate_data_t *d = arg; if (d) { tor_free(d->cell); - connection_or_remove_from_identity_map(d->c1); - connection_or_remove_from_identity_map(d->c2); + connection_or_clear_identity(d->c1); + connection_or_clear_identity(d->c2); connection_free_(TO_CONN(d->c1)); connection_free_(TO_CONN(d->c2)); circuitmux_free(d->chan2->base_.cmux); @@ -742,6 +1188,7 @@ static void * authenticate_data_setup(const struct testcase_t *test) { authenticate_data_t *d = tor_malloc_zero(sizeof(*d)); + int is_ed = d->is_ed = (test->setup_data == (void*)3); scheduler_init(); @@ -751,6 +1198,7 @@ authenticate_data_setup(const struct testcase_t *test) MOCK(tor_tls_get_tlssecrets, mock_get_tlssecrets); MOCK(connection_or_close_for_error, mock_close_for_err); MOCK(channel_set_circid_type, mock_set_circid_type); + MOCK(tor_tls_export_key_material, mock_export_key_material); d->c1 = or_connection_new(CONN_TYPE_OR, AF_INET); d->c2 = or_connection_new(CONN_TYPE_OR, AF_INET); tor_addr_from_ipv4h(&d->c1->base_.addr, 0x01020304); @@ -761,6 +1209,8 @@ authenticate_data_setup(const struct testcase_t *test) tt_int_op(tor_tls_context_init(TOR_TLS_CTX_IS_PUBLIC_SERVER, d->key1, d->key2, 86400), ==, 0); + init_mock_ed_keys(d->key2); + d->c1->base_.state = OR_CONN_STATE_OR_HANDSHAKING_V3; d->c1->link_proto = 3; tt_int_op(connection_init_or_handshake_state(d->c1, 1), ==, 0); @@ -791,21 +1241,37 @@ authenticate_data_setup(const struct testcase_t *test) const uint8_t *der; size_t sz; tor_x509_cert_get_der(id_cert, &der, &sz); - d->c1->handshake_state->id_cert = tor_x509_cert_decode(der, sz); - d->c2->handshake_state->id_cert = tor_x509_cert_decode(der, sz); + d->c1->handshake_state->certs->id_cert = tor_x509_cert_decode(der, sz); + d->c2->handshake_state->certs->id_cert = tor_x509_cert_decode(der, sz); + + if (is_ed) { + d->c1->handshake_state->certs->ed_id_sign = + tor_cert_dup(get_master_signing_key_cert()); + d->c2->handshake_state->certs->ed_id_sign = + tor_cert_dup(get_master_signing_key_cert()); + d->c2->handshake_state->certs->ed_sign_auth = + tor_cert_dup(get_current_auth_key_cert()); + } else { + tt_assert(! tor_tls_get_my_certs(0, &auth_cert, &id_cert)); + tor_x509_cert_get_der(auth_cert, &der, &sz); + d->c2->handshake_state->certs->auth_cert = tor_x509_cert_decode(der, sz); + } tor_x509_cert_get_der(link_cert, &der, &sz); mock_peer_cert = tor_x509_cert_decode(der, sz); tt_assert(mock_peer_cert); + mock_own_cert = tor_x509_cert_decode(der, sz); tt_assert(mock_own_cert); - tt_assert(! tor_tls_get_my_certs(0, &auth_cert, &id_cert)); - tor_x509_cert_get_der(auth_cert, &der, &sz); - d->c2->handshake_state->auth_cert = tor_x509_cert_decode(der, sz); /* Make an authenticate cell ... */ - tt_int_op(0, ==, connection_or_send_authenticate_cell(d->c1, - AUTHTYPE_RSA_SHA256_TLSSECRET)); + int authtype; + if (is_ed) + authtype = AUTHTYPE_ED25519_SHA256_RFC5705; + else + authtype = AUTHTYPE_RSA_SHA256_TLSSECRET; + tt_int_op(0, ==, connection_or_send_authenticate_cell(d->c1, authtype)); + tt_assert(mock_got_var_cell); d->cell = mock_got_var_cell; mock_got_var_cell = NULL; @@ -831,42 +1297,64 @@ test_link_handshake_auth_cell(void *arg) /* Is the cell well-formed on the outer layer? */ tt_int_op(d->cell->command, ==, CELL_AUTHENTICATE); tt_int_op(d->cell->payload[0], ==, 0); - tt_int_op(d->cell->payload[1], ==, 1); + if (d->is_ed) + tt_int_op(d->cell->payload[1], ==, 3); + else + tt_int_op(d->cell->payload[1], ==, 1); tt_int_op(ntohs(get_uint16(d->cell->payload + 2)), ==, d->cell->payload_len - 4); /* Check it out for plausibility... */ auth_ctx_t ctx; - ctx.is_ed = 0; + ctx.is_ed = d->is_ed; tt_int_op(d->cell->payload_len-4, ==, auth1_parse(&auth1, d->cell->payload+4, d->cell->payload_len - 4, &ctx)); tt_assert(auth1); - tt_mem_op(auth1->type, ==, "AUTH0001", 8); + if (d->is_ed) { + tt_mem_op(auth1->type, ==, "AUTH0003", 8); + } else { + tt_mem_op(auth1->type, ==, "AUTH0001", 8); + } tt_mem_op(auth1->tlssecrets, ==, "int getRandomNumber(){return 4;}", 32); - tt_int_op(auth1_getlen_sig(auth1), >, 120); /* Is the signature okay? */ - uint8_t sig[128]; - uint8_t digest[32]; - - auth_pubkey = tor_tls_cert_get_key(d->c2->handshake_state->auth_cert); - int n = crypto_pk_public_checksig( + const uint8_t *start = d->cell->payload+4, *end = auth1->end_of_signed; + if (d->is_ed) { + ed25519_signature_t sig; + tt_int_op(auth1_getlen_sig(auth1), ==, ED25519_SIG_LEN); + memcpy(&sig.sig, auth1_getarray_sig(auth1), ED25519_SIG_LEN); + tt_assert(!ed25519_checksig(&sig, start, end-start, + &get_current_auth_keypair()->pubkey)); + } else { + uint8_t sig[128]; + uint8_t digest[32]; + tt_int_op(auth1_getlen_sig(auth1), >, 120); + auth_pubkey = tor_tls_cert_get_key( + d->c2->handshake_state->certs->auth_cert); + int n = crypto_pk_public_checksig( auth_pubkey, (char*)sig, sizeof(sig), (char*)auth1_getarray_sig(auth1), auth1_getlen_sig(auth1)); - tt_int_op(n, ==, 32); - const uint8_t *start = d->cell->payload+4, *end = auth1->end_of_signed; - crypto_digest256((char*)digest, - (const char*)start, end-start, DIGEST_SHA256); - tt_mem_op(sig, ==, digest, 32); + tt_int_op(n, ==, 32); + crypto_digest256((char*)digest, + (const char*)start, end-start, DIGEST_SHA256); + tt_mem_op(sig, ==, digest, 32); + } /* Then feed it to c2. */ tt_int_op(d->c2->handshake_state->authenticated, ==, 0); channel_tls_process_authenticate_cell(d->cell, d->chan2); tt_int_op(mock_close_called, ==, 0); tt_int_op(d->c2->handshake_state->authenticated, ==, 1); + if (d->is_ed) { + tt_int_op(d->c2->handshake_state->authenticated_ed25519, ==, 1); + tt_int_op(d->c2->handshake_state->authenticated_rsa, ==, 1); + } else { + tt_int_op(d->c2->handshake_state->authenticated_ed25519, ==, 0); + tt_int_op(d->c2->handshake_state->authenticated_rsa, ==, 1); + } done: auth1_free(auth1); @@ -900,7 +1388,8 @@ AUTHENTICATE_FAIL(badproto, d->c2->link_proto = 2) AUTHENTICATE_FAIL(atclient, require_failure_message = "We originated this connection"; - d->c2->handshake_state->started_here = 1) + d->c2->handshake_state->started_here = 1; + d->c2->handshake_state->certs->started_here = 1;) AUTHENTICATE_FAIL(duplicate, require_failure_message = "We already got one"; d->c2->handshake_state->received_authenticate = 1) @@ -924,13 +1413,13 @@ AUTHENTICATE_FAIL(nocerts, AUTHENTICATE_FAIL(noidcert, require_failure_message = "We never got an identity " "certificate"; - tor_x509_cert_free(d->c2->handshake_state->id_cert); - d->c2->handshake_state->id_cert = NULL) + tor_x509_cert_free(d->c2->handshake_state->certs->id_cert); + d->c2->handshake_state->certs->id_cert = NULL) AUTHENTICATE_FAIL(noauthcert, - require_failure_message = "We never got an authentication " - "certificate"; - tor_x509_cert_free(d->c2->handshake_state->auth_cert); - d->c2->handshake_state->auth_cert = NULL) + require_failure_message = "We never got an RSA " + "authentication certificate"; + tor_x509_cert_free(d->c2->handshake_state->certs->auth_cert); + d->c2->handshake_state->certs->auth_cert = NULL) AUTHENTICATE_FAIL(tooshort, require_failure_message = "Cell was way too short"; d->cell->payload_len = 3) @@ -954,11 +1443,33 @@ AUTHENTICATE_FAIL(badcontent, "cell body was not as expected"; d->cell->payload[10] ^= 0xff) AUTHENTICATE_FAIL(badsig_1, - require_failure_message = "Signature wasn't valid"; + if (d->is_ed) + require_failure_message = "Ed25519 signature wasn't valid"; + else + require_failure_message = "RSA signature wasn't valid"; d->cell->payload[d->cell->payload_len - 5] ^= 0xff) - -#define TEST(name, flags) \ - { #name , test_link_handshake_ ## name, (flags), NULL, NULL } +AUTHENTICATE_FAIL(missing_ed_id, + { + tor_cert_free(d->c2->handshake_state->certs->ed_id_sign); + d->c2->handshake_state->certs->ed_id_sign = NULL; + require_failure_message = "Ed authenticate without Ed ID " + "cert from peer"; + }) +AUTHENTICATE_FAIL(missing_ed_auth, + { + tor_cert_free(d->c2->handshake_state->certs->ed_sign_auth); + d->c2->handshake_state->certs->ed_sign_auth = NULL; + require_failure_message = "We never got an Ed25519 " + "authentication certificate"; + }) + +#define TEST_RSA(name, flags) \ + { #name , test_link_handshake_ ## name, (flags), \ + &passthrough_setup, (void*)"RSA" } + +#define TEST_ED(name, flags) \ + { #name "_ed25519" , test_link_handshake_ ## name, (flags), \ + &passthrough_setup, (void*)"Ed25519" } #define TEST_RCV_AUTHCHALLENGE(name) \ { "recv_authchallenge/" #name , \ @@ -968,17 +1479,34 @@ AUTHENTICATE_FAIL(badsig_1, #define TEST_RCV_CERTS(name) \ { "recv_certs/" #name , \ test_link_handshake_recv_certs_ ## name, TT_FORK, \ - &setup_recv_certs, NULL } + &setup_recv_certs, (void*)"RSA-Link" } + +#define TEST_RCV_CERTS_RSA(name,type) \ + { "recv_certs/" #name , \ + test_link_handshake_recv_certs_ ## name, TT_FORK, \ + &setup_recv_certs, (void*)type } + +#define TEST_RCV_CERTS_ED(name, type) \ + { "recv_certs/" #name "_ed25519", \ + test_link_handshake_recv_certs_ ## name, TT_FORK, \ + &setup_recv_certs, (void*)type } #define TEST_AUTHENTICATE(name) \ { "authenticate/" #name , test_link_handshake_auth_ ## name, TT_FORK, \ &setup_authenticate, NULL } +#define TEST_AUTHENTICATE_ED(name) \ + { "authenticate/" #name "_ed25519" , test_link_handshake_auth_ ## name, \ + TT_FORK, &setup_authenticate, (void*)3 } + struct testcase_t link_handshake_tests[] = { - TEST(certs_ok, TT_FORK), - //TEST(certs_bad, TT_FORK), + TEST_RSA(certs_ok, TT_FORK), + TEST_ED(certs_ok, TT_FORK), + TEST_RCV_CERTS(ok), - TEST_RCV_CERTS(ok_server), + TEST_RCV_CERTS_ED(ok, "Ed25519-Link"), + TEST_RCV_CERTS_RSA(ok_server, "RSA-Auth"), + TEST_RCV_CERTS_ED(ok_server, "Ed25519-Auth"), TEST_RCV_CERTS(badstate), TEST_RCV_CERTS(badproto), TEST_RCV_CERTS(duplicate), @@ -988,18 +1516,41 @@ struct testcase_t link_handshake_tests[] = { TEST_RCV_CERTS(truncated_1), TEST_RCV_CERTS(truncated_2), TEST_RCV_CERTS(truncated_3), + TEST_RCV_CERTS_ED(truncated_4, "Ed25519-Link"), + TEST_RCV_CERTS_ED(truncated_5, "Ed25519-Link"), + TEST_RCV_CERTS_ED(truncated_6, "Ed25519-Link"), + TEST_RCV_CERTS_ED(truncated_7, "Ed25519-Link"), TEST_RCV_CERTS(not_x509), TEST_RCV_CERTS(both_link), TEST_RCV_CERTS(both_id_rsa), TEST_RCV_CERTS(both_auth), + TEST_RCV_CERTS_ED(duplicate_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(duplicate_link, "Ed25519-Link"), + TEST_RCV_CERTS_ED(duplicate_crosscert, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_crosscert, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_signing_key, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_link, "Ed25519-Link"), + TEST_RCV_CERTS_ED(missing_auth, "Ed25519-Auth"), + TEST_RCV_CERTS_ED(missing_rsa_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(link_mismatch, "Ed25519-Link"), + TEST_RCV_CERTS_ED(bad_ed_sig, "Ed25519-Link"), + TEST_RCV_CERTS_ED(bad_rsa_id_cert, "Ed25519-Link"), + TEST_RCV_CERTS_ED(bad_crosscert, "Ed25519-Link"), + TEST_RCV_CERTS_RSA(expired_rsa_id, "RSA-Link"), + TEST_RCV_CERTS_ED(expired_rsa_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(expired_ed_id, "Ed25519-Link"), + TEST_RCV_CERTS_ED(expired_ed_link, "Ed25519-Link"), + TEST_RCV_CERTS_ED(expired_crosscert, "Ed25519-Link"), TEST_RCV_CERTS(wrong_labels_1), TEST_RCV_CERTS(wrong_labels_2), TEST_RCV_CERTS(wrong_labels_3), TEST_RCV_CERTS(server_missing_certs), TEST_RCV_CERTS(server_wrong_labels_1), - TEST(send_authchallenge, TT_FORK), + TEST_RSA(send_authchallenge, TT_FORK), TEST_RCV_AUTHCHALLENGE(ok), + TEST_RCV_AUTHCHALLENGE(ok_ed25519), TEST_RCV_AUTHCHALLENGE(ok_noserver), TEST_RCV_AUTHCHALLENGE(ok_unrecognized), TEST_RCV_AUTHCHALLENGE(badstate), @@ -1012,6 +1563,7 @@ struct testcase_t link_handshake_tests[] = { TEST_RCV_AUTHCHALLENGE(nonzero_circid), TEST_AUTHENTICATE(cell), + TEST_AUTHENTICATE_ED(cell), TEST_AUTHENTICATE(badstate), TEST_AUTHENTICATE(badproto), TEST_AUTHENTICATE(atclient), @@ -1027,6 +1579,9 @@ struct testcase_t link_handshake_tests[] = { TEST_AUTHENTICATE(tooshort_1), TEST_AUTHENTICATE(badcontent), TEST_AUTHENTICATE(badsig_1), + TEST_AUTHENTICATE_ED(badsig_1), + TEST_AUTHENTICATE_ED(missing_ed_id), + TEST_AUTHENTICATE_ED(missing_ed_auth), //TEST_AUTHENTICATE(), END_OF_TESTCASES diff --git a/src/test/test_oom.c b/src/test/test_oom.c index 6102af01f5..0f97972032 100644 --- a/src/test/test_oom.c +++ b/src/test/test_oom.c @@ -15,6 +15,7 @@ #include "config.h" #include "relay.h" #include "test.h" +#include "test_helpers.h" /* small replacement mock for circuit_mark_for_close_ to avoid doing all * the other bookkeeping that comes with marking circuits. */ @@ -58,24 +59,6 @@ dummy_or_circuit_new(int n_p_cells, int n_n_cells) return TO_CIRCUIT(circ); } -static circuit_t * -dummy_origin_circuit_new(int n_cells) -{ - origin_circuit_t *circ = origin_circuit_new(); - int i; - cell_t cell; - - for (i=0; i < n_cells; ++i) { - crypto_rand((void*)&cell, sizeof(cell)); - cell_queue_append_packed_copy(TO_CIRCUIT(circ), - &TO_CIRCUIT(circ)->n_chan_cells, - 1, &cell, 1, 0); - } - - TO_CIRCUIT(circ)->purpose = CIRCUIT_PURPOSE_C_GENERAL; - return TO_CIRCUIT(circ); -} - static void add_bytes_to_buf(buf_t *buf, size_t n_bytes) { diff --git a/src/test/test_options.c b/src/test/test_options.c index e85e11805b..d5782e9ec0 100644 --- a/src/test/test_options.c +++ b/src/test/test_options.c @@ -18,6 +18,7 @@ #include "sandbox.h" #include "memarea.h" #include "policies.h" +#include "test_helpers.h" #define NS_MODULE test_options @@ -332,7 +333,8 @@ fixed_get_uname(void) "VirtualAddrNetworkIPv4 127.192.0.0/10\n" \ "VirtualAddrNetworkIPv6 [FE80::]/10\n" \ "SchedulerHighWaterMark__ 42\n" \ - "SchedulerLowWaterMark__ 10\n" + "SchedulerLowWaterMark__ 10\n" \ + "UseEntryGuards 1\n" typedef struct { or_options_t *old_opt; @@ -650,16 +652,18 @@ test_options_validate__authdir(void *ignored) setup_capture_of_logs(LOG_INFO); options_test_data_t *tdata = get_options_test_data( "AuthoritativeDirectory 1\n" - "Address this.should.not_exist.example.org"); + "Address this.should.not!exist!.example.org"); sandbox_disable_getaddrinfo_cache(); + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + UNMOCK(tor_addr_lookup); tt_int_op(ret, OP_EQ, -1); tt_str_op(msg, OP_EQ, "Failed to resolve/guess local address. See logs for" " details."); expect_log_msg("Could not resolve local Address " - "'this.should.not_exist.example.org'. Failing.\n"); + "'this.should.not!exist!.example.org'. Failing.\n"); tor_free(msg); free_options_test_data(tdata); @@ -1018,7 +1022,7 @@ test_options_validate__transproxy(void *ignored) ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, -1); -#if !defined(__OpenBSD__) && !defined( DARWIN ) +#if !defined(OpenBSD) && !defined( DARWIN ) tt_str_op(msg, OP_EQ, "pf-divert is a OpenBSD-specific and OS X/Darwin-specific feature."); #else @@ -1087,7 +1091,7 @@ test_options_validate__transproxy(void *ignored) if (msg) { TT_DIE(("Expected NULL but got '%s'", msg)); } -#elif defined(__OpenBSD__) +#elif defined(OpenBSD) tdata = get_options_test_data("TransProxyType pf-divert\n" "TransPort 127.0.0.1:123\n"); ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); @@ -1794,14 +1798,6 @@ test_options_validate__reachable_addresses(void *ignored) /* Test IPv4-only clients setting IPv6 preferences */ -#define WARN_PLEASE_USE_IPV6_OR_LOG_MSG \ - "ClientPreferIPv6ORPort 1 is ignored unless tor is using IPv6. " \ - "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges.\n" - -#define WARN_PLEASE_USE_IPV6_DIR_LOG_MSG \ - "ClientPreferIPv6DirPort 1 is ignored unless tor is using IPv6. " \ - "Please set ClientUseIPv6 1, ClientUseIPv4 0, or configure bridges.\n" - free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "ClientUseIPv4 1\n" @@ -1811,7 +1807,6 @@ test_options_validate__reachable_addresses(void *ignored) ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, 0); - expect_log_msg(WARN_PLEASE_USE_IPV6_OR_LOG_MSG); tor_free(msg); free_options_test_data(tdata); @@ -1823,7 +1818,6 @@ test_options_validate__reachable_addresses(void *ignored) ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); tt_int_op(ret, OP_EQ, 0); - expect_log_msg(WARN_PLEASE_USE_IPV6_DIR_LOG_MSG); tor_free(msg); /* Now test an IPv4/IPv6 client setting IPv6 preferences */ @@ -1945,6 +1939,19 @@ test_options_validate__use_bridges(void *ignored) tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "UseBridges 1\n" "Bridge 10.0.0.1\n" + "UseEntryGuards 0\n" + ); + + ret = options_validate(tdata->old_opt, tdata->opt, tdata->def_opt, 0, &msg); + tt_int_op(ret, OP_EQ, -1); + tt_str_op(msg, OP_EQ, + "Setting UseBridges requires also setting UseEntryGuards."); + tor_free(msg); + + free_options_test_data(tdata); + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES + "UseBridges 1\n" + "Bridge 10.0.0.1\n" "Bridge !!!\n" ); @@ -2819,8 +2826,8 @@ test_options_validate__single_onion(void *ignored) tt_int_op(ret, OP_EQ, -1); tt_str_op(msg, OP_EQ, "HiddenServiceNonAnonymousMode is incompatible with " "using Tor as an anonymous client. Please set " - "Socks/Trans/NATD/DNSPort to 0, or HiddenServiceNonAnonymousMode " - "to 0, or use the non-anonymous Tor2webMode."); + "Socks/Trans/NATD/DNSPort to 0, or revert " + "HiddenServiceNonAnonymousMode to 0."); tor_free(msg); free_options_test_data(tdata); @@ -3037,6 +3044,7 @@ test_options_validate__proxy(void *ignored) options_test_data_t *tdata = NULL; sandbox_disable_getaddrinfo_cache(); setup_capture_of_logs(LOG_WARN); + MOCK(tor_addr_lookup, mock_tor_addr_lookup__fail_on_bad_addrs); free_options_test_data(tdata); tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES @@ -3057,6 +3065,7 @@ test_options_validate__proxy(void *ignored) tor_free(msg); free_options_test_data(tdata); + tdata = get_options_test_data(TEST_OPTIONS_DEFAULT_VALUES "HttpProxy not_so_valid!\n" ); @@ -3357,6 +3366,7 @@ test_options_validate__proxy(void *ignored) policies_free_all(); // sandbox_free_getaddrinfo_cache(); tor_free(msg); + UNMOCK(tor_addr_lookup); } static void diff --git a/src/test/test_policy.c b/src/test/test_policy.c index 1ffdc2cd51..e86d0f0274 100644 --- a/src/test/test_policy.c +++ b/src/test/test_policy.c @@ -1031,10 +1031,10 @@ test_policies_general(void *arg) { char *policy_strng = NULL; smartlist_t *chunks = smartlist_new(); - smartlist_add(chunks, tor_strdup("accept ")); + smartlist_add_strdup(chunks, "accept "); for (i=1; i<10000; ++i) smartlist_add_asprintf(chunks, "%d,", i); - smartlist_add(chunks, tor_strdup("20000")); + smartlist_add_strdup(chunks, "20000"); policy_strng = smartlist_join_strings(chunks, "", 0, NULL); SMARTLIST_FOREACH(chunks, char *, ch, tor_free(ch)); smartlist_free(chunks); @@ -1048,9 +1048,9 @@ test_policies_general(void *arg) for (i=1; i<2000; i+=2) { char buf[POLICY_BUF_LEN]; tor_snprintf(buf, sizeof(buf), "reject *:%d", i); - smartlist_add(sm, tor_strdup(buf)); + smartlist_add_strdup(sm, buf); } - smartlist_add(sm, tor_strdup("accept *:*")); + smartlist_add_strdup(sm, "accept *:*"); policy_str = smartlist_join_strings(sm, ",", 0, NULL); test_policy_summary_helper( policy_str, "accept 2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40,42,44," @@ -1587,8 +1587,12 @@ test_policies_getinfo_helper_policies(void *arg) append_exit_policy_string(&mock_my_routerinfo.exit_policy, "reject *6:*"); mock_options.IPv6Exit = 1; - tor_addr_from_ipv4h(&mock_options.OutboundBindAddressIPv4_, TEST_IPV4_ADDR); - tor_addr_parse(&mock_options.OutboundBindAddressIPv6_, TEST_IPV6_ADDR); + tor_addr_from_ipv4h( + &mock_options.OutboundBindAddresses[OUTBOUND_ADDR_EXIT][0], + TEST_IPV4_ADDR); + tor_addr_parse( + &mock_options.OutboundBindAddresses[OUTBOUND_ADDR_EXIT][1], + TEST_IPV6_ADDR); mock_options.ExitPolicyRejectPrivate = 1; mock_options.ExitPolicyRejectLocalInterfaces = 1; diff --git a/src/test/test_pt.c b/src/test/test_pt.c index e5cdc5f3cd..f93019f1c4 100644 --- a/src/test/test_pt.c +++ b/src/test/test_pt.c @@ -155,9 +155,9 @@ test_pt_get_transport_options(void *arg) opt_str = get_transport_options_for_server_proxy(mp); tt_ptr_op(opt_str, OP_EQ, NULL); - smartlist_add(mp->transports_to_launch, tor_strdup("gruyere")); - smartlist_add(mp->transports_to_launch, tor_strdup("roquefort")); - smartlist_add(mp->transports_to_launch, tor_strdup("stnectaire")); + smartlist_add_strdup(mp->transports_to_launch, "gruyere"); + smartlist_add_strdup(mp->transports_to_launch, "roquefort"); + smartlist_add_strdup(mp->transports_to_launch, "stnectaire"); tt_assert(options); @@ -305,7 +305,7 @@ tor_get_lines_from_handle_replacement(STDIN_HANDLE *handle, smartlist_add_asprintf(retval_sl, "SMETHOD mock%d 127.0.0.1:555%d", times_called, times_called); } else { - smartlist_add(retval_sl, tor_strdup("SMETHODS DONE")); + smartlist_add_strdup(retval_sl, "SMETHODS DONE"); } return retval_sl; diff --git a/src/test/test_rendcache.c b/src/test/test_rendcache.c index a5d3f351f8..0d53c78817 100644 --- a/src/test/test_rendcache.c +++ b/src/test/test_rendcache.c @@ -10,6 +10,7 @@ #include "router.h" #include "routerlist.h" #include "config.h" +#include "hs_common.h" #include <openssl/rsa.h> #include "rend_test_helpers.h" #include "log_test_helpers.h" @@ -24,15 +25,16 @@ static const int TIME_IN_THE_FUTURE = REND_CACHE_MAX_SKEW + 60; static rend_data_t * mock_rend_data(const char *onion_address) { - rend_data_t *rend_query = tor_malloc_zero(sizeof(rend_data_t)); + rend_data_v2_t *v2_data = tor_malloc_zero(sizeof(*v2_data)); + rend_data_t *rend_query = &v2_data->base_; + rend_query->version = 2; - strlcpy(rend_query->onion_address, onion_address, - sizeof(rend_query->onion_address)); - rend_query->auth_type = REND_NO_AUTH; + strlcpy(v2_data->onion_address, onion_address, + sizeof(v2_data->onion_address)); + v2_data->auth_type = REND_NO_AUTH; rend_query->hsdirs_fp = smartlist_new(); smartlist_add(rend_query->hsdirs_fp, tor_memdup("aaaaaaaaaaaaaaaaaaaaaaaa", DIGEST_LEN)); - return rend_query; } @@ -144,7 +146,8 @@ test_rend_cache_store_v2_desc_as_client(void *data) // Test mismatch between service ID and onion address rend_cache_init(); - strncpy(mock_rend_query->onion_address, "abc", REND_SERVICE_ID_LEN_BASE32+1); + strncpy(TO_REND_DATA_V2(mock_rend_query)->onion_address, "abc", + REND_SERVICE_ID_LEN_BASE32+1); ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, mock_rend_query, NULL); @@ -155,12 +158,16 @@ test_rend_cache_store_v2_desc_as_client(void *data) // Test incorrect descriptor ID rend_cache_init(); mock_rend_query = mock_rend_data(service_id); - desc_id_base32[0]++; + char orig = desc_id_base32[0]; + if (desc_id_base32[0] == 'a') + desc_id_base32[0] = 'b'; + else + desc_id_base32[0] = 'a'; ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, desc_id_base32, mock_rend_query, NULL); tt_int_op(ret, OP_EQ, -1); - desc_id_base32[0]--; + desc_id_base32[0] = orig; rend_cache_free_all(); // Test too old descriptor @@ -230,9 +237,9 @@ test_rend_cache_store_v2_desc_as_client(void *data) generate_desc(RECENT_TIME, &desc_holder, &service_id, 3); mock_rend_query = mock_rend_data(service_id); - mock_rend_query->auth_type = REND_BASIC_AUTH; + TO_REND_DATA_V2(mock_rend_query)->auth_type = REND_BASIC_AUTH; client_cookie[0] = 'A'; - memcpy(mock_rend_query->descriptor_cookie, client_cookie, + memcpy(TO_REND_DATA_V2(mock_rend_query)->descriptor_cookie, client_cookie, REND_DESC_COOKIE_LEN); base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN); @@ -250,7 +257,7 @@ test_rend_cache_store_v2_desc_as_client(void *data) generate_desc(RECENT_TIME, &desc_holder, &service_id, 3); mock_rend_query = mock_rend_data(service_id); - mock_rend_query->auth_type = REND_BASIC_AUTH; + TO_REND_DATA_V2(mock_rend_query)->auth_type = REND_BASIC_AUTH; base32_encode(desc_id_base32, sizeof(desc_id_base32), desc_holder->desc_id, DIGEST_LEN); ret = rend_cache_store_v2_desc_as_client(desc_holder->desc_str, @@ -1078,9 +1085,10 @@ static void test_rend_cache_clean_v2_descs_as_dir(void *data) { rend_cache_entry_t *e; - time_t now; + time_t now, cutoff; rend_service_descriptor_t *desc; now = time(NULL); + cutoff = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW); const char key[DIGEST_LEN] = "abcde"; (void)data; @@ -1088,7 +1096,7 @@ test_rend_cache_clean_v2_descs_as_dir(void *data) rend_cache_init(); // Test running with an empty cache - rend_cache_clean_v2_descs_as_dir(now, 0); + rend_cache_clean_v2_descs_as_dir(cutoff); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0); // Test with only one new entry @@ -1100,38 +1108,15 @@ test_rend_cache_clean_v2_descs_as_dir(void *data) e->parsed = desc; digestmap_set(rend_cache_v2_dir, key, e); - rend_cache_clean_v2_descs_as_dir(now, 0); + /* Set the cutoff to minus 10 seconds. */ + rend_cache_clean_v2_descs_as_dir(cutoff - 10); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1); // Test with one old entry - desc->timestamp = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000); - rend_cache_clean_v2_descs_as_dir(now, 0); - tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0); - - // Test with one entry that has an old last served - e = tor_malloc_zero(sizeof(rend_cache_entry_t)); - e->last_served = now - (REND_CACHE_MAX_AGE + REND_CACHE_MAX_SKEW + 1000); - desc = tor_malloc_zero(sizeof(rend_service_descriptor_t)); - desc->timestamp = now; - desc->pk = pk_generate(0); - e->parsed = desc; - digestmap_set(rend_cache_v2_dir, key, e); - - rend_cache_clean_v2_descs_as_dir(now, 0); + desc->timestamp = cutoff - 1000; + rend_cache_clean_v2_descs_as_dir(cutoff); tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 0); - // Test a run through asking for a large force_remove - e = tor_malloc_zero(sizeof(rend_cache_entry_t)); - e->last_served = now; - desc = tor_malloc_zero(sizeof(rend_service_descriptor_t)); - desc->timestamp = now; - desc->pk = pk_generate(0); - e->parsed = desc; - digestmap_set(rend_cache_v2_dir, key, e); - - rend_cache_clean_v2_descs_as_dir(now, 20000); - tt_int_op(digestmap_size(rend_cache_v2_dir), OP_EQ, 1); - done: rend_cache_free_all(); } diff --git a/src/test/test_routerkeys.c b/src/test/test_routerkeys.c index 24b0da1c46..13059267ac 100644 --- a/src/test/test_routerkeys.c +++ b/src/test/test_routerkeys.c @@ -450,8 +450,8 @@ test_routerkeys_ed_keys_init_all(void *arg) options->DataDirectory = dir; - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_assert(get_master_identity_key()); tt_assert(get_master_identity_key()); tt_assert(get_master_signing_keypair()); @@ -466,7 +466,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Call load_ed_keys again, but nothing has changed. */ tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_mem_op(&auth, ==, get_current_auth_keypair(), sizeof(auth)); @@ -474,8 +474,8 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Force a reload: we make new link/auth keys. */ routerkeys_free_all(); - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -489,7 +489,7 @@ test_routerkeys_ed_keys_init_all(void *arg) /* Force a link/auth-key regeneration by advancing time. */ tt_int_op(0, ==, load_ed_keys(options, now+3*86400)); - tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400)); + tt_int_op(0, ==, generate_ed_link_cert(options, now+3*86400, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -502,8 +502,8 @@ test_routerkeys_ed_keys_init_all(void *arg) memcpy(&auth, get_current_auth_keypair(), sizeof(auth)); /* Force a signing-key regeneration by advancing time. */ - tt_int_op(0, ==, load_ed_keys(options, now+100*86400)); - tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400)); + tt_int_op(1, ==, load_ed_keys(options, now+100*86400)); + tt_int_op(0, ==, generate_ed_link_cert(options, now+100*86400, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, !=, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -520,8 +520,8 @@ test_routerkeys_ed_keys_init_all(void *arg) routerkeys_free_all(); unlink(get_fname("test_ed_keys_init_all/keys/" "ed25519_master_id_secret_key")); - tt_int_op(0, ==, load_ed_keys(options, now)); - tt_int_op(0, ==, generate_ed_link_cert(options, now)); + tt_int_op(1, ==, load_ed_keys(options, now)); + tt_int_op(0, ==, generate_ed_link_cert(options, now, 0)); tt_mem_op(&id, ==, get_master_identity_key(), sizeof(id)); tt_mem_op(&sign, ==, get_master_signing_keypair(), sizeof(sign)); tt_assert(! tor_cert_eq(link_cert, get_current_link_cert_cert())); @@ -614,6 +614,66 @@ test_routerkeys_cross_certify_tap(void *args) crypto_pk_free(onion_key); } +static void +test_routerkeys_rsa_ed_crosscert(void *arg) +{ + (void)arg; + ed25519_public_key_t ed; + crypto_pk_t *rsa = pk_generate(2); + + uint8_t *cc = NULL; + ssize_t cc_len; + time_t expires_in = 1470846177; + + tt_int_op(0, OP_EQ, ed25519_public_from_base64(&ed, + "ThisStringCanContainAnythingSoNoKeyHereNowX")); + cc_len = tor_make_rsa_ed25519_crosscert(&ed, rsa, expires_in, &cc); + + tt_int_op(cc_len, OP_GT, 0); + tt_int_op(cc_len, OP_GT, 37); /* key, expires, siglen */ + tt_mem_op(cc, OP_EQ, ed.pubkey, 32); + time_t expires_out = 3600 * ntohl(get_uint32(cc+32)); + tt_int_op(expires_out, OP_GE, expires_in); + tt_int_op(expires_out, OP_LE, expires_in + 3600); + + tt_int_op(cc_len, OP_EQ, 37 + get_uint8(cc+36)); + + tt_int_op(0, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_in - 10)); + + /* Now try after it has expired */ + tt_int_op(-4, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_out + 1)); + + /* Truncated object */ + tt_int_op(-2, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len - 2, rsa, &ed, + expires_in - 10)); + + /* Key not as expected */ + cc[0] ^= 3; + tt_int_op(-3, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_in - 10)); + cc[0] ^= 3; + + /* Bad signature */ + cc[40] ^= 3; + tt_int_op(-5, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_in - 10)); + cc[40] ^= 3; + + /* Signature of wrong data */ + cc[0] ^= 3; + ed.pubkey[0] ^= 3; + tt_int_op(-6, OP_EQ, rsa_ed25519_crosscert_check(cc, cc_len, rsa, &ed, + expires_in - 10)); + cc[0] ^= 3; + ed.pubkey[0] ^= 3; + + done: + crypto_pk_free(rsa); + tor_free(cc); +} + #define TEST(name, flags) \ { #name , test_routerkeys_ ## name, (flags), NULL, NULL } @@ -626,6 +686,7 @@ struct testcase_t routerkeys_tests[] = { TEST(ed_keys_init_all, TT_FORK), TEST(cross_certify_ntor, 0), TEST(cross_certify_tap, 0), + TEST(rsa_ed_crosscert, 0), END_OF_TESTCASES }; diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c index 088bd257c3..78f1cf16b7 100644 --- a/src/test/test_routerlist.c +++ b/src/test/test_routerlist.c @@ -15,6 +15,7 @@ #include "container.h" #include "directory.h" #include "dirvote.h" +#include "entrynodes.h" #include "microdesc.h" #include "networkstatus.h" #include "nodelist.h" @@ -335,30 +336,6 @@ test_router_pick_directory_server_impl(void *arg) node_router1->is_valid = 1; node_router3->is_valid = 1; - flags |= PDS_FOR_GUARD; - node_router1->using_as_guard = 1; - node_router2->using_as_guard = 1; - node_router3->using_as_guard = 1; - rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); - tt_assert(rs == NULL); - node_router1->using_as_guard = 0; - rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); - tt_assert(rs != NULL); - tt_assert(tor_memeq(rs->identity_digest, router1_id, DIGEST_LEN)); - rs = NULL; - node_router2->using_as_guard = 0; - node_router3->using_as_guard = 0; - - /* One not valid, one guard. This should leave one remaining */ - node_router1->is_valid = 0; - node_router2->using_as_guard = 1; - rs = router_pick_directory_server_impl(V3_DIRINFO, flags, NULL); - tt_assert(rs != NULL); - tt_assert(tor_memeq(rs->identity_digest, router3_id, DIGEST_LEN)); - rs = NULL; - node_router1->is_valid = 1; - node_router2->using_as_guard = 0; - /* Manipulate overloaded */ node_router2->rs->last_dir_503_at = now; @@ -420,6 +397,7 @@ test_router_pick_directory_server_impl(void *arg) done: UNMOCK(usable_consensus_flavor); + if (router1_id) tor_free(router1_id); if (router2_id) diff --git a/src/test/test_routerset.c b/src/test/test_routerset.c index 1b526d430b..7efd042ed5 100644 --- a/src/test/test_routerset.c +++ b/src/test/test_routerset.c @@ -623,7 +623,7 @@ NS(test_main)(void *arg) (void)arg; tgt = routerset_new(); - smartlist_add(src->list, tor_strdup("{xx}")); + smartlist_add_strdup(src->list, "{xx}"); routerset_union(tgt, src); tt_int_op(smartlist_len(tgt->list), OP_NE, 0); @@ -745,7 +745,7 @@ NS(test_main)(void *arg) tt_int_op(is_empty, OP_NE, 0); set = routerset_new(); - smartlist_add(set->list, tor_strdup("{xx}")); + smartlist_add_strdup(set->list, "{xx}"); is_empty = routerset_is_empty(set); routerset_free(set); set = NULL; @@ -1616,7 +1616,7 @@ NS(test_main)(void *arg) NS_MOCK(node_get_by_nickname); NS(mock_nickname) = "foo"; - smartlist_add(set->list, tor_strdup(NS(mock_nickname))); + smartlist_add_strdup(set->list, NS(mock_nickname)); routerset_get_all_nodes(out, set, NULL, 0); out_len = smartlist_len(out); @@ -1667,7 +1667,7 @@ NS(test_main)(void *arg) NS(mock_node).is_running = 0; NS(mock_nickname) = "foo"; - smartlist_add(set->list, tor_strdup(NS(mock_nickname))); + smartlist_add_strdup(set->list, NS(mock_nickname)); routerset_get_all_nodes(out, set, NULL, 1); out_len = smartlist_len(out); @@ -1766,7 +1766,7 @@ NS(test_main)(void *arg) NS_MOCK(nodelist_get_list); - smartlist_add(set->country_names, tor_strdup("{xx}")); + smartlist_add_strdup(set->country_names, "{xx}"); NS(mock_smartlist) = smartlist_new(); routerset_get_all_nodes(out, set, NULL, 1); @@ -1813,7 +1813,7 @@ NS(test_main)(void *arg) NS_MOCK(nodelist_get_list); - smartlist_add(set->country_names, tor_strdup("{xx}")); + smartlist_add_strdup(set->country_names, "{xx}"); NS(mock_smartlist) = smartlist_new(); NS(mock_node).is_running = 0; smartlist_add(NS(mock_smartlist), (void *)&NS(mock_node)); @@ -1985,7 +1985,7 @@ NS(test_main)(void *arg) int r; (void)arg; - smartlist_add(b->list, tor_strdup("{xx}")); + smartlist_add_strdup(b->list, "{xx}"); r = routerset_equal(a, b); routerset_free(a); routerset_free(b); @@ -2010,9 +2010,9 @@ NS(test_main)(void *arg) int r; (void)arg; - smartlist_add(a->list, tor_strdup("{aa}")); - smartlist_add(b->list, tor_strdup("{b1}")); - smartlist_add(b->list, tor_strdup("{b2}")); + smartlist_add_strdup(a->list, "{aa}"); + smartlist_add_strdup(b->list, "{b1}"); + smartlist_add_strdup(b->list, "{b2}"); r = routerset_equal(a, b); routerset_free(a); routerset_free(b); @@ -2037,8 +2037,8 @@ NS(test_main)(void *arg) int r; (void)arg; - smartlist_add(a->list, tor_strdup("foo")); - smartlist_add(b->list, tor_strdup("bar")); + smartlist_add_strdup(a->list, "foo"); + smartlist_add_strdup(b->list, "bar"); r = routerset_equal(a, b); routerset_free(a); routerset_free(b); @@ -2063,8 +2063,8 @@ NS(test_main)(void *arg) int r; (void)arg; - smartlist_add(a->list, tor_strdup("foo")); - smartlist_add(b->list, tor_strdup("foo")); + smartlist_add_strdup(a->list, "foo"); + smartlist_add_strdup(b->list, "foo"); r = routerset_equal(a, b); routerset_free(a); routerset_free(b); diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c index 056f199b94..026a0f3825 100644 --- a/src/test/test_shared_random.c +++ b/src/test/test_shared_random.c @@ -48,7 +48,7 @@ init_authority_state(void) mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL); tt_assert(mock_cert); options->AuthoritativeDir = 1; - tt_int_op(0, ==, load_ed_keys(options, time(NULL))); + tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0); sr_state_init(0, 0); /* It's possible a commit has been generated in our state depending on * the phase we are currently in which uses "now" as the starting @@ -286,7 +286,7 @@ test_sr_commit(void *arg) tt_assert(auth_cert); options->AuthoritativeDir = 1; - tt_int_op(0, ==, load_ed_keys(options, now)); + tt_int_op(load_ed_keys(options, time(NULL)), OP_GE, 0); } /* Generate our commit object and validate it has the appropriate field @@ -348,12 +348,12 @@ test_sr_commit(void *arg) /* We'll build a list of values from our commit that our parsing function * takes from a vote line and see if we can parse it correctly. */ { - smartlist_add(args, tor_strdup("1")); - smartlist_add(args, - tor_strdup(crypto_digest_algorithm_get_name(our_commit->alg))); - smartlist_add(args, tor_strdup(sr_commit_get_rsa_fpr(our_commit))); - smartlist_add(args, tor_strdup(our_commit->encoded_commit)); - smartlist_add(args, tor_strdup(our_commit->encoded_reveal)); + smartlist_add_strdup(args, "1"); + smartlist_add_strdup(args, + crypto_digest_algorithm_get_name(our_commit->alg)); + smartlist_add_strdup(args, sr_commit_get_rsa_fpr(our_commit)); + smartlist_add_strdup(args, our_commit->encoded_commit); + smartlist_add_strdup(args, our_commit->encoded_reveal); parsed_commit = sr_parse_commit(args); tt_assert(parsed_commit); /* That parsed commit should be _EXACTLY_ like our original commit (we diff --git a/src/test/test_tortls.c b/src/test/test_tortls.c index 47455cff83..4bfcea211d 100644 --- a/src/test/test_tortls.c +++ b/src/test/test_tortls.c @@ -1091,13 +1091,13 @@ test_tortls_check_lifetime(void *ignored) time_t now = time(NULL); tls = tor_malloc_zero(sizeof(tor_tls_t)); - ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); tt_int_op(ret, OP_EQ, -1); tls->ssl = tor_malloc_zero(sizeof(SSL)); tls->ssl->session = tor_malloc_zero(sizeof(SSL_SESSION)); tls->ssl->session->peer = validCert; - ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, 0); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, 0); tt_int_op(ret, OP_EQ, 0); ASN1_STRING_free(validCert->cert_info->validity->notBefore); @@ -1105,10 +1105,10 @@ test_tortls_check_lifetime(void *ignored) ASN1_STRING_free(validCert->cert_info->validity->notAfter); validCert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, now+60); - ret = tor_tls_check_lifetime(LOG_WARN, tls, 0, -1000); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), 0, -1000); tt_int_op(ret, OP_EQ, -1); - ret = tor_tls_check_lifetime(LOG_WARN, tls, -1000, 0); + ret = tor_tls_check_lifetime(LOG_WARN, tls, time(NULL), -1000, 0); tt_int_op(ret, OP_EQ, -1); done: @@ -2658,18 +2658,18 @@ test_tortls_cert_is_valid(void *ignored) tor_x509_cert_t *cert = NULL, *scert = NULL; scert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 0); cert = tor_malloc_zero(sizeof(tor_x509_cert_t)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 0); tor_free(scert); tor_free(cert); cert = tor_x509_cert_new(read_cert_from(validCertString)); scert = tor_x509_cert_new(read_cert_from(caCertString)); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 1); #ifndef OPENSSL_OPAQUE @@ -2680,7 +2680,7 @@ test_tortls_cert_is_valid(void *ignored) ASN1_TIME_free(cert->cert->cert_info->validity->notAfter); cert->cert->cert_info->validity->notAfter = ASN1_TIME_set(NULL, time(NULL)-1000000); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 0); tor_x509_cert_free(cert); @@ -2689,7 +2689,7 @@ test_tortls_cert_is_valid(void *ignored) scert = tor_x509_cert_new(read_cert_from(caCertString)); X509_PUBKEY_free(cert->cert->cert_info->key); cert->cert->cert_info->key = NULL; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); tt_int_op(ret, OP_EQ, 0); #endif @@ -2700,7 +2700,7 @@ test_tortls_cert_is_valid(void *ignored) scert = tor_x509_cert_new(read_cert_from(caCertString)); /* This doesn't actually change the key in the cert. XXXXXX */ BN_one(EVP_PKEY_get1_RSA(X509_get_pubkey(cert->cert))->n); - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); tt_int_op(ret, OP_EQ, 0); tor_x509_cert_free(cert); @@ -2709,7 +2709,7 @@ test_tortls_cert_is_valid(void *ignored) scert = tor_x509_cert_new(read_cert_from(caCertString)); /* This doesn't actually change the key in the cert. XXXXXX */ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 1); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 1); tt_int_op(ret, OP_EQ, 0); tor_x509_cert_free(cert); @@ -2718,7 +2718,7 @@ test_tortls_cert_is_valid(void *ignored) scert = tor_x509_cert_new(read_cert_from(caCertString)); /* This doesn't actually change the key in the cert. XXXXXX */ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 1); tor_x509_cert_free(cert); @@ -2728,7 +2728,7 @@ test_tortls_cert_is_valid(void *ignored) /* This doesn't actually change the key in the cert. XXXXXX */ X509_get_pubkey(cert->cert)->type = EVP_PKEY_EC; X509_get_pubkey(cert->cert)->ameth = NULL; - ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, 0); + ret = tor_tls_cert_is_valid(LOG_WARN, cert, scert, time(NULL), 0); tt_int_op(ret, OP_EQ, 0); #endif diff --git a/src/test/test_util.c b/src/test/test_util.c index fcda564569..e80201737a 100644 --- a/src/test/test_util.c +++ b/src/test/test_util.c @@ -9,6 +9,7 @@ #define CONTROL_PRIVATE #define UTIL_PRIVATE #include "or.h" +#include "buffers.h" #include "config.h" #include "control.h" #include "test.h" @@ -1059,6 +1060,23 @@ test_util_time(void *arg) tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04 00:48:22.100", &t_res)); tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04 00:48:22XYZ", &t_res)); + /* but... that _is_ acceptable if we aren't being strict. */ + t_res = 0; + i = parse_iso_time_("2004-08-04 00:48:22XYZ", &t_res, 0, 0); + tt_int_op(0,OP_EQ, i); + tt_int_op(t_res,OP_EQ, (time_t)1091580502UL); + + /* try nospace variant. */ + t_res = 0; + i = parse_iso_time_nospace("2004-08-04T00:48:22", &t_res); + tt_int_op(0,OP_EQ, i); + tt_int_op(t_res,OP_EQ, (time_t)1091580502UL); + + tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04T00:48:22", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time_nospace("2004-08-04 00:48:22", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time("2004-08-04x00:48:22", &t_res)); + tt_int_op(-1,OP_EQ, parse_iso_time_nospace("2004-08-04x00:48:22", &t_res)); + /* Test tor_gettimeofday */ end.tv_sec = 4; @@ -5074,7 +5092,8 @@ test_util_socket(void *arg) fd1 = tor_open_socket_with_extensions(domain, SOCK_STREAM, 0, 0, 0); int err = tor_socket_errno(fd1); - if (fd1 < 0 && err == SOCK_ERRNO(EPROTONOSUPPORT)) { + if (fd1 < 0 && (err == SOCK_ERRNO(EPROTONOSUPPORT) || + err == SOCK_ERRNO(EAFNOSUPPORT))) { /* Assume we're on an IPv4-only or IPv6-only system, and give up now. */ goto done; } @@ -5462,26 +5481,26 @@ test_util_calloc_check(void *arg) { (void) arg; /* Easy cases that are good. */ - tt_assert(size_mul_check__(0,0)); - tt_assert(size_mul_check__(0,100)); - tt_assert(size_mul_check__(100,0)); - tt_assert(size_mul_check__(100,100)); + tt_assert(size_mul_check(0,0)); + tt_assert(size_mul_check(0,100)); + tt_assert(size_mul_check(100,0)); + tt_assert(size_mul_check(100,100)); /* Harder cases that are still good. */ - tt_assert(size_mul_check__(SIZE_MAX, 1)); - tt_assert(size_mul_check__(1, SIZE_MAX)); - tt_assert(size_mul_check__(SIZE_MAX / 10, 9)); - tt_assert(size_mul_check__(11, SIZE_MAX / 12)); + tt_assert(size_mul_check(SIZE_MAX, 1)); + tt_assert(size_mul_check(1, SIZE_MAX)); + tt_assert(size_mul_check(SIZE_MAX / 10, 9)); + tt_assert(size_mul_check(11, SIZE_MAX / 12)); const size_t sqrt_size_max_p1 = ((size_t)1) << (sizeof(size_t) * 4); - tt_assert(size_mul_check__(sqrt_size_max_p1, sqrt_size_max_p1 - 1)); + tt_assert(size_mul_check(sqrt_size_max_p1, sqrt_size_max_p1 - 1)); /* Cases that overflow */ - tt_assert(! size_mul_check__(SIZE_MAX, 2)); - tt_assert(! size_mul_check__(2, SIZE_MAX)); - tt_assert(! size_mul_check__(SIZE_MAX / 10, 11)); - tt_assert(! size_mul_check__(11, SIZE_MAX / 10)); - tt_assert(! size_mul_check__(SIZE_MAX / 8, 9)); - tt_assert(! size_mul_check__(sqrt_size_max_p1, sqrt_size_max_p1)); + tt_assert(! size_mul_check(SIZE_MAX, 2)); + tt_assert(! size_mul_check(2, SIZE_MAX)); + tt_assert(! size_mul_check(SIZE_MAX / 10, 11)); + tt_assert(! size_mul_check(11, SIZE_MAX / 10)); + tt_assert(! size_mul_check(SIZE_MAX / 8, 9)); + tt_assert(! size_mul_check(sqrt_size_max_p1, sqrt_size_max_p1)); done: ; @@ -5612,6 +5631,33 @@ test_util_monotonic_time_ratchet(void *arg) ; } +static void +test_util_htonll(void *arg) +{ + (void)arg; +#ifdef WORDS_BIGENDIAN + const uint64_t res_be = 0x8877665544332211; +#else + const uint64_t res_le = 0x1122334455667788; +#endif + + tt_u64_op(0, OP_EQ, tor_htonll(0)); + tt_u64_op(0, OP_EQ, tor_ntohll(0)); + tt_u64_op(UINT64_MAX, OP_EQ, tor_htonll(UINT64_MAX)); + tt_u64_op(UINT64_MAX, OP_EQ, tor_ntohll(UINT64_MAX)); + +#ifdef WORDS_BIGENDIAN + tt_u64_op(res_be, OP_EQ, tor_htonll(0x8877665544332211)); + tt_u64_op(res_be, OP_EQ, tor_ntohll(0x8877665544332211)); +#else + tt_u64_op(res_le, OP_EQ, tor_htonll(0x8877665544332211)); + tt_u64_op(res_le, OP_EQ, tor_ntohll(0x8877665544332211)); +#endif + + done: + ; +} + #define UTIL_LEGACY(name) \ { #name, test_util_ ## name , 0, NULL, NULL } @@ -5705,6 +5751,7 @@ struct testcase_t util_tests[] = { UTIL_TEST(calloc_check, 0), UTIL_TEST(monotonic_time, 0), UTIL_TEST(monotonic_time_ratchet, TT_FORK), + UTIL_TEST(htonll, 0), END_OF_TESTCASES }; diff --git a/src/test/test_util_format.c b/src/test/test_util_format.c index 63a668238c..21a6923c6d 100644 --- a/src/test/test_util_format.c +++ b/src/test/test_util_format.c @@ -11,25 +11,14 @@ #define NS_MODULE util_format -#if !defined(HAVE_HTONLL) && !defined(htonll) -#ifdef WORDS_BIGENDIAN -#define htonll(x) (x) -#else -static uint64_t -htonll(uint64_t a) -{ - return htonl((uint32_t)(a>>32)) | (((uint64_t)htonl((uint32_t)a))<<32); -} -#endif -#endif - static void test_util_format_unaligned_accessors(void *ignored) { (void)ignored; char buf[9] = "onionsoup"; // 6f6e696f6e736f7570 - tt_u64_op(get_uint64(buf+1), OP_EQ, htonll(U64_LITERAL(0x6e696f6e736f7570))); + tt_u64_op(get_uint64(buf+1), OP_EQ, + tor_htonll(U64_LITERAL(0x6e696f6e736f7570))); tt_uint_op(get_uint32(buf+1), OP_EQ, htonl(0x6e696f6e)); tt_uint_op(get_uint16(buf+1), OP_EQ, htons(0x6e69)); tt_uint_op(get_uint8(buf+1), OP_EQ, 0x6e); @@ -43,7 +32,7 @@ test_util_format_unaligned_accessors(void *ignored) set_uint32(buf+1, htonl(0x78696465)); tt_mem_op(buf, OP_EQ, "oxidestop", 9); - set_uint64(buf+1, htonll(U64_LITERAL(0x6266757363617465))); + set_uint64(buf+1, tor_htonll(U64_LITERAL(0x6266757363617465))); tt_mem_op(buf, OP_EQ, "obfuscate", 9); done: ; @@ -213,6 +202,9 @@ test_util_format_base64_decode(void *ignored) res = base64_decode(dst, SIZE_T_CEILING+1, src, 10); tt_int_op(res, OP_EQ, -1); + res = base64_decode(dst, 1, real_src, SIZE_MAX/3+1); + tt_int_op(res, OP_EQ, -1); + const char *s = "T3BhIG11bmRv"; res = base64_decode(dst, 9, s, strlen(s)); tt_int_op(res, OP_EQ, 9); diff --git a/src/test/testing_common.c b/src/test/testing_common.c index 9c6580f788..caeae13a38 100644 --- a/src/test/testing_common.c +++ b/src/test/testing_common.c @@ -178,65 +178,6 @@ remove_directory(void) rm_rf(temp_dir); } -/** Define this if unit tests spend too much time generating public keys*/ -#define CACHE_GENERATED_KEYS - -#define N_PREGEN_KEYS 11 -static crypto_pk_t *pregen_keys[N_PREGEN_KEYS]; -static int next_key_idx; - -/** Generate and return a new keypair for use in unit tests. If we're using - * the key cache optimization, we might reuse keys. "idx" is ignored. - * Our only guarantee is that we won't reuse a key till this function has been - * called several times. The order in which keys are returned is slightly - * randomized, so that tests that depend on a particular order will not be - * reliable. */ -crypto_pk_t * -pk_generate(int idx) -{ - (void) idx; -#ifdef CACHE_GENERATED_KEYS - /* Either skip 1 or 2 keys. */ - next_key_idx += crypto_rand_int_range(1,3); - next_key_idx %= N_PREGEN_KEYS; - return crypto_pk_dup_key(pregen_keys[next_key_idx]); -#else - crypto_pk_t *result; - int res; - result = crypto_pk_new(); - res = crypto_pk_generate_key__real(result); - tor_assert(!res); - return result; -#endif -} - -#ifdef CACHE_GENERATED_KEYS -static int -crypto_pk_generate_key_with_bits__get_cached(crypto_pk_t *env, int bits) -{ - if (bits != 1024) - return crypto_pk_generate_key_with_bits__real(env, bits); - - crypto_pk_t *newkey = pk_generate(0); - crypto_pk_assign_(env, newkey); - crypto_pk_free(newkey); - return 0; -} -#endif - -/** Free all storage used for the cached key optimization. */ -static void -free_pregenerated_keys(void) -{ - unsigned idx; - for (idx = 0; idx < N_PREGEN_KEYS; ++idx) { - if (pregen_keys[idx]) { - crypto_pk_free(pregen_keys[idx]); - pregen_keys[idx] = NULL; - } - } -} - static void * passthrough_test_setup(const struct testcase_t *testcase) { @@ -365,15 +306,7 @@ main(int c, const char **v) } tor_set_failed_assertion_callback(an_assertion_failed); -#ifdef CACHE_GENERATED_KEYS - for (i = 0; i < N_PREGEN_KEYS; ++i) { - pregen_keys[i] = crypto_pk_new(); - int r = crypto_pk_generate_key(pregen_keys[i]); - tor_assert(r == 0); - } - MOCK(crypto_pk_generate_key_with_bits, - crypto_pk_generate_key_with_bits__get_cached); -#endif + init_pregenerated_keys(); atexit(remove_directory); diff --git a/src/test/testing_rsakeys.c b/src/test/testing_rsakeys.c new file mode 100644 index 0000000000..134770bb0d --- /dev/null +++ b/src/test/testing_rsakeys.c @@ -0,0 +1,546 @@ +/* Copyright (c) 2001-2004, Roger Dingledine. + * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson. + * Copyright (c) 2007-2016, The Tor Project, Inc. */ +/* See LICENSE for licensing information */ + +#include "orconfig.h" +#include "or.h" +#include "test.h" + +/** Define this if unit tests spend too much time generating public keys. + * This module is meant to save time by using a bunch of pregenerated RSA +keys among */ +#define USE_PREGENERATED_RSA_KEYS + +#ifdef USE_PREGENERATED_RSA_KEYS + +static const char *PREGEN_KEYS_1024[] = { +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICWwIBAAKBgQCZa39BCgq7KWBWFSjGYHhqmTCHvQ7WNEFAb9Mujb6Xn/Zy01fu\n" +"WIpVvqmAKeLNEziItUm/gB8GwAN+/ZLwL9pufjIp2Ar+yqVXKySioZQxuCgTP2wm\n" +"Ku0OfmAra1Xbtrkc2OCJllxkyNPrJ/kxfwjWR96UP0+VMbOlkBoEH1FtvwIDAQAB\n" +"AoGAUXoygeMIYe+OdwkTt48CRHKIwH3aRE5KHSOGPyIOB05vvvmYqD8jcHgqYqNc\n" +"DNdZXdkRin9LevU8phObFq4DTXp08XggUx4Kk4AdsFKubQtJ8gHm3xlSKbZXX2m/\n" +"ZF0GRaZtVDQ3TRGh+OBLILt/2jT+BaFKGAyJ7al76F2nprECQQDJyLlteLDFBmrd\n" +"0kAjNBE50S5YskBCQeQACROfyTKW8lG1J57UBeYjXvbrDFBR4alIS9DEexGai9Gz\n" +"wxpgKg2nAkEAwqQmPstjHxvqGQRi41uXO026MLxY7dhEqs1aSw3tuT8v17pW3OEa\n" +"Qxv7JINePZ3+sNN+Ic+3RXBR0QuD7lSSKQJAZjVSF21GvMXfY7SX4D0DbLHUNAE2\n" +"I1mUz5/JXOpgwazETmpfPS4vwELd93kpRhBz2rbsbFmaNRoVgmSU+5jRiQJAZ1bV\n" +"g2NilgKxEGU2x3U6Xt8Oqo9lO6omEvUCKnUTsNWuZf/l3FGbKuQxO5qPr3Ex5tny\n" +"zqrEqBZRKgbOHfxCuQJAbJY5C3Nm5koemr031r00MY2YD1b6+hyKZyPdZ21HpyY8\n" +"z1kWShL0POjYPX/BnKE1FkpklWcKBb7wkK7dvAKkEQ==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQCyqMM2TfFGV5tVBTVabxLVln8146nDavIdR6q78DCUMh8Zfzkk\n" +"h9Lbl1NX4RU+AmrCZMPq21/EjIRxRQyRdgPYJVLdp96eGeYnEzmMkqvXiswXvDg/\n" +"tXqsjyJeYsoHMQWDTpCLfjYo4K1ol1sg8VIs4wQeq5og6QSdmhBoz7MyqQIDAQAB\n" +"AoGBAIJekey7nZeV8Bxva4ptSRIg+v0I/2VBUiG5nUX9NIW/uV/yrXERx/VDjKaw\n" +"8b5JJzxpKWnk4RJc83xwRYaT1qMYHiQfybxEI0K9SjhtaThAjtXkQGtZgLJILl3t\n" +"yh3LPTh1ocwafsKjU6eGYAe/DYn9/QwYHbtyaimcigu4etp9AkEA2DgC+HndoP1i\n" +"np26Lx+4TG0vAfrVYGSLT9FXwf2iBV3oJvdKqu6wr8ipb1SbshRPcOQd31/mCh6+\n" +"2BR+d4ddcwJBANOHrlBbGZdHnoEu6kKbPwwkc31IZYqyfSpkqm0Lb2oWZ9SInKfc\n" +"cz0qpH91p610XUpYmycaJr4K+N8jgrz86HMCQQCoqGBg1Ca2OpCf66bctWB8dTqS\n" +"z8d7rlIhC8npr1+f0hWRt5pN5Wx7YgoQpq3gZgllpPtMT7DQOhVh1fKkaDnTAkA4\n" +"XuskPPLX7t0dvhvtviOSH9CrLXTp/mD+wC7uumJpmij3aaSd01DelxOZaAhUYDNQ\n" +"UcafKAf1E0V5aaQ4qwljAkA9NVN6CtpzzcLrstTKxrx5P1Ylt/0UYQDo1lIaqwrT\n" +"aOFbXmOungiC9+p/4U7RbX0MEzjFDHCWlaHASviGVgta\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICWwIBAAKBgQDDt2V63APj3JSqaRgofUzhtB+prm0wII4uHyxfOxnpYIELOW5z\n" +"3UHmkr+B4D+Nif5jIp0i6W4OS4S+YHewKsDsXvXKRIW78KzOt6Le4JI9rSarNjy5\n" +"aJKksWQRALLCmxP/BdolaBFqF3fIPD5+Zxu8ESgxhkEQI4p7awUp3E730QIDAQAB\n" +"AoGAZktfAR4p8lkCYydW9yK2ommQ+xEuBK+fYL/uYz/yxSYpjIJSFsEYhrlA21Mo\n" +"JIRxr8MRuoOjgFk8YnztUeimuHpslDlZDaCBzjRjBRFCMepZNG9xqSEL0u7C+SH6\n" +"KU5f2x2P6PneBj6WaHZM+6Lf2xHlOoeuaVSUfq2Pk2VBF9kCQQDtawWWNwP0+xea\n" +"oCAQpanaLzYPjlqZfHJQ1AAI5eSkdf1qmlypIHwOtjAEa6XuEO/Or8RNkNy4nQdw\n" +"qhcQ7PXDAkEA0wjT6Z+Lrt67FnwPgoSvl4Nukcqw4OWHbBKhaQPsO9+oc3PAXLdD\n" +"SclUUqDF6NX1yONTV1KrPdz4zElmEua+2wJABm4inZnp2oW+cuqpU6oY+pbSwQMb\n" +"AxMyyWukgJkxYx7q+SsrHU2K7p8Sl9wOh28f/5oVGAC3aayfGfcRXtz8HwJAIqeO\n" +"dQzYGU1GF7kjquEzHIRewd4xEZ1fkaW1j9MvFd3ygZL+gbsud41yJWd1WHjaNbTu\n" +"2KYgrLX+vT1IX844hQJAbg0V7iHlttQqXL7yN09jIjQLprqVhDZCUHS9s9Dxe7fz\n" +"Ac0ZZD0D6EVNmSmBB71q7kLUWX/W/10d447TLnnfew==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXgIBAAKBgQDhCAjPEockl4lqkvoIb5O3NJJG8NWD31c63e/cPWY6MX5nOM/q\n" +"avof2eWJxFOk0HQ2BRVwIgNex6kLxtsdw7XE0A5uZorTp9DbRCGMqUqHNhHH9ci2\n" +"mMPP9jptq3ieWg310bH4Tad8h3WE2npSCDBvxyV6EmuH2rlQW9ZlHNoiRQIDAQAB\n" +"AoGBAI4PgWggPTqng7PJF5mNvsYQpSutzE0VCL977nmuNUQVjMPjRLarVD4ZU+QW\n" +"EevhQQv9R5xjjJcgGqL5pchzjeKDm0/LA+AygnZoDMs2O68Neieqvr7cPqr5ALGs\n" +"WuZvSn+bRJTenvV9sUh2ii0/u3GQbL1v7GWDkIdD7itDbmRhAkEA8iijuEY+W67w\n" +"7JusjY2MQ2Cm6xxxR0YcnYPzT6UDm+Z7NNJwKscQ6AjayNmxmXGpbUdukzLzXf8y\n" +"fccI9t6iHQJBAO3kx9nZay0Ktl51QP5o2gwoqRIbnogGfR06KJOlzIPGR0aPn8cg\n" +"uKq2SiyjewEaSBM6S/4UlxYUmvc3VKnxCEkCQQDpTjg2YQ7RPGIIRA/iLV7Wx3bq\n" +"C/QjjCwjoi44LK6mdE9928WPoUzrkSRg4EQYpwZqL6kcDrmkdSuLPMipOGQNAkA3\n" +"KtzlujPOiDNuiEaAORSHyU4b8ue6p7aP9pK+Wq6oyGxzAo+NABuTCx78ZxT5Vnzs\n" +"aJKC44d+CV0+g0hQ+KJxAkEAqFYzNWIzTHX8DVDdK9BpUaBg1DFxIeP5Kk+/X3FF\n" +"5BafG08B6OiLf8qIGGsxLXNRjIE0GVp3Sy23FUKtUymP+A==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQDMDk01VwPxQq/BAwOBmfGUP/x5BQn+uxI0Aat6bdWuz/2CsjbS\n" +"CWD/YLCaPm+DpHp9RMwk4HONJaw4B2XOw3ELPx7y9DEgdC1wZ9wRkJmqr2IJZoZR\n" +"C7x43nNv+/IXTiRkkljCcMpoL1Tld+L2VbmWR29PdZwvspWRILkEZu1mNwIDAQAB\n" +"AoGANvFK3KfXSei4xfF3yjeXEmHAKx2uOUZJenNQpqBYPr+F9ODjXd5knZ59LqrM\n" +"/9cTnBMgHHXK5yBTpKppQSjikLeQ2BF04Ktff9oGqVcS9x/rKo0CREuxsEfawZOW\n" +"OzOWENp4YcDKGP1I/Ctr185QzStaWrXVQftxmYQ53T77ShECQQDnhabwtqW7rfe4\n" +"+MfkWEJ9Y2s6iMs3JWnwPOX9G9R39PiAD4vAghHJyHHttS9Ipxmvp0hThu0x7a4g\n" +"8BfUpqgjAkEA4aFAmzarWKigREAACVTYH2RHpXbuk05vF9WqfMPiEvQUd5a1q6vc\n" +"xkGZsE3v/TExLjPRZP4FeUNV5sD7THzA3QJBAJxPoRlNx3GCEAlDdfnWGPX9JI09\n" +"hC40RWUcSI7ttjJTI1+an1kWuBnLChhaRpU/tFjikTNLmmMmPHUihIRfDI8CQG7g\n" +"3WzpKr8A7vFbOilbxnF2yDaqAYfmTXW7DHMPl/OUetJh/5kDdhT/e9VGF5+nIvH/\n" +"iPFGW85Bpt8lCtmFnQkCQQDjpp9iy2qesE7KKX4Kv3++QfCJ2w3g7lwg4iyncoDd\n" +"JrM53p29HROM21R6eekvqeWIe9tEX754b+E/N60ZjpGm\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXgIBAAKBgQDdDn3H+Eu0AW5GKohqDBntw6ubnd3VaJwZGzZyga4J2kLg8peP\n" +"RAW6GDD6pcHzW+KZbFWHtRk70FSwvmyGcf+DY0r5tfyCHyDGmbJyPR0o6OVCgSFl\n" +"ccf4eDvbyszzMdlx3uL05ABIpCShoKtEUqvyIQla3Jon+QBwuVkizMzyVwIDAQAB\n" +"AoGACoKh4Fwh3VEkGRn0mnYw1Wk0Q5Xh8j+jDF6K3C7mQ3mpLGDca+dkDlEQIxq2\n" +"egeoYnsQJf+qT3m8TRsAtfO9nj7+7IX4BfCtdIi4RNcorbs5YMWtFyaywnM6SQjS\n" +"+1qf74aL4On9WRO2FtvnTMjFAAkiWNbQp7mWwTmB59i620ECQQDwde6/PwhUzvZh\n" +"dyslKJdna5RjkDQyDIuh0zD/tFZ0Iko7Luec8q6n52ev/n0OiTLGetUh8goePsPP\n" +"HVZHidNJAkEA61eMCmmu+GCAg2vJRtL5sDakAXsbP5M9Bf/QVHXtc4EVXHC6T2ld\n" +"bldOJriNbBThBuPNmlQbssn9FApkyWT4nwJBAIuHIv3+CUuMvBJaH8L0BsaP+g67\n" +"wk24Ud2Yujnl3rSMoR4uXV8IwqfS8quAs/gXTEs3QyzrUUuzh9NKZqIkK2ECQQCz\n" +"vivBEDKIlPvSZBJYO25kfXcJgoKvLb9fw5/TwjXXD/HGpnpFiI3JZnjT7gRlVhT/\n" +"9CDmC/MTvF3EXqPXhXy1AkEAo3a2me23Ljmub21jycSKaCk09dK85QTRRMe9c/hs\n" +"i+pcGi9ZZW0Mm7cyQo47oXjNurkkv0fEvXIobVTEXAGU7w==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQCv8R1IbfYnE3R3kNeezJ7m02XnyCBDDy0YfrQldQ+urdg1CFye\n" +"bO0iPniJb8fmV8NW7x6nUZTDznCg+igroKXtK/w0WYmJJiH4A7Oi5xNjAfRIPvJ/\n" +"J5GI8szS8rH8tp8pW1h8k/kNg2pnBjwQ2U9omhp95RGaHDQSRYzzH/fEFQIDAQAB\n" +"AoGAcy7+BcH/iZuB/xjzIIJDcUhqibCJ9n0D/+pLU85sYuZrCmUcBZe4M1gEn61v\n" +"iExilRJc1hthskL/l1POYql8lk+aqeeDuh38fWJj60TCV/sENiuXOsTmoFVA5pNn\n" +"lwlG8JlpBMsgr1fGqg1C/WLFfMmvXdKVGvpRqI06j7AYUa0CQQDfZ5rI+FhXBlxo\n" +"PR5CM1LB90DuHUMW+Kqoj0c9d2esXEQM7UqQ/9BiBQbL6Py7Z3VwCxibOqyz7+V7\n" +"2aGUMAKnAkEAyZy5Mu2tHs6YBBxPYam7huzMUYjddN7ixAZUyGwxQp9kTIF2NbSQ\n" +"yVDjKrco3s2lO4qj4pSumwVe3GGlsi6G4wJAOOS3pIqqZK84BUvbUtyjLMZ9AKbv\n" +"GQCG5ZpneB3ahyiQJAKiRL8BIJVLH87b3hYA8GHDCHUu2jwz4xCPd5+qbQJAV0TP\n" +"pYvb9AnZI25drhiaY7z8dA6aTYxs/A0Bhf/PEteLwtIHKRgP1BR/QG4n8slxTGSm\n" +"q91P9ypL9XkPECGzoQJBAIMvGEM7ZGevQHBjJ8HhU8IsgT4cYH/XEYb8jRy4F+Ui\n" +"jKxHPxLuFK4urAZunNUNrqhT0PxbB7hRjtHZrmFkrcc=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQDSpmV8ncLwc8gXzdFsZGPDtMO7C/IN9jKCIK13WIseMg1APlMt\n" +"PB5lMQ9fa3m9ZRU0L8HzRo+u/Xdos3yIBI38X2Avy0laGKnQxiOKaDT/5ZHeiBBh\n" +"nMZjP2WY5V1sgqNP9RD8enE6WaSvq1j0BM++mn9KEe//5+dWD8tboBKF4QIDAQAB\n" +"AoGBALgVoerdE1Z+WAY1XyaSNHz6o3H6ZnW9CTaex/jb7/dbVikmThnhx842qXCB\n" +"w8m3ZGhOs/edWkNaTde5wsI6+LhVGco/PWxN4v61jokxUU+5KvUvGacXhXIjzKwG\n" +"DrNCYmle62QCI1z4+TLQW/Lq+jw2Wzk70NWEvoP58gt5SJoBAkEA9wubRKRs49LW\n" +"5JNQZ9hjc+mAfP9YK/sMe4jkdloMMWXjSMlF3Z4mI9XQSpfbBqwWIBXsjU/15LIS\n" +"ftmujZsMKQJBANpJEZI7UFoRdSP7AlM0YJuXWnVGyn/K+VIeEso5AlZdKXCTpxqp\n" +"9blWq0UVC6jLesZ5UNPuBiAnrBaVwDA8YvkCQF+FQVfdK607TJO80g4VAP9EfcXX\n" +"BUScIUtytsN8NdKzzpnKGRWDnMOmXI87ABkoWLW3RGuvSyhOIhCiInfmR2ECQASc\n" +"FmroJcJBLCAeZOYs7P1cLOTdIdmhB7LcP7lVit8YCJAADj9Z536KfgNvdleSNH2M\n" +"glB3blmvfMrdTrm2DMECQQDj6GJ/Tc2rCsq534xknasVjrgtJMQFxmQCTVgBx9pc\n" +"gTflJAHAmNDvstacVqeObLCF2ZIvya8fSXGbDOJYeGDv\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQDGgUJAm7vf/3focNGwzv4TkzYF2XwpAirnb61dyxvfug1zKv2k\n" +"AUg3qACiurR7JrI+kAbmxEnNaKV7ts7uO763wP9KE8YAuFZsp7NFA295rEZhw38T\n" +"rUlWHMCeaZ3mqW2q8gA14C/ZJCG4gS91SIHLjNGsbHwr2Jvri2ItwIP8FQIDAQAB\n" +"AoGAONceb32oiHWQkkBr6uL6ogRPPdGO2fdC7c5uqCLWsnOGEmpHAsVTNoym0fIA\n" +"aBsmgv+e2klukKDccdZg3prA+z7lHcc2a4bIFguF6ei80hLIis/dds66fFXofCzy\n" +"DMlkncSbJwIvQHG9gblxp9qSKElZF7XjABZEImarfUlakGkCQQD//msGy5N0ZhMI\n" +"yGMXkwXRJXfmRrIrOqHx6u1eUp4OuqDW+hBz4KCHnWfuRJkNGQIammSf18jPasP5\n" +"YHyr/LifAkEAxoJ8R8Vusexo9ZjuU44qXCSvJQ26UBV7mn6TGEAn2DRK1RWKDaHv\n" +"j2vnRjt3CO9WPDQL7SB/1HNAy+dIMPyqywJBAIB6tESIz8zPniX+TJ18UKMTZwXP\n" +"3YQMvVKpUdDRLjq+OBMtFizSRD9MJOlUzGvibUfkzTPcHRDcyNbUMj4vbIkCQBx4\n" +"6sqAjvgGKKfRX52sbnb47AYsieSisC/gp8h6qzxfg7w8cqix6WJw36M7ND+b1Iqe\n" +"DHfeiXc3cLvOWJRuKTECQCEYkujtSjXWb26xaESFWGtUI/nEvCyqYPQAFBpaGzQ3\n" +"tiTDeKHzypesWYoTxOiNQWCQMLrFGuUbDpYOuDOVNjw=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQCcwSAfytnspSSDX/sKmCPOMnpuCYeWA4wbz1wLyb63a8/KXhhG\n" +"6o2W0kt3x1vnGZkeWwZOeBFUqwoc+xHhoNcZFsMOyqbqA3UMZW5cx27MsexRTQHs\n" +"Go1newu/E+8NNCohY51G7z1Hdo0L6mi/Tldh7puuGsMwKqNG/Vvo/GQDgwIDAQAB\n" +"AoGBAIUdpBAbjXDe1OET0vYuOMnUKA/l29RS8tpy/zGrg1/0GCM8QNWIPfEEaL4w\n" +"+CSKonMazYI5iE4kaZQuygKXOdFqKxX8nrGK2hR0DIEUHhhiqyGMUKrf4ELkAJzK\n" +"tHtcO64OFEU2EGa72wCmyk2MhqhLxWxA7E00x24uvW6pen6xAkEAzHhbzlRgLZ+K\n" +"QuXmQHEqkGaS2Ccf6c9TA5Bf5S2/5zBl+OqVyJJQH0yrbPYR6Nn1NeSv3R4IDJYg\n" +"fSZLaVzWHQJBAMRCU6QtTnZoQ97pLvXCSKRYKJF+CnE3zDFTyoJrpK0W1FSnb1EE\n" +"DWjjdSdMLynf/InX+VOaLk3Gxwjme4NKjh8CQQCg2b4/HplayrsVzY3I/D2jw02Z\n" +"xY2RfYusrhMCU284DBbsLn8OfiuRs9rXqOyF5ZDFiNXgeROT8zYzvcBtbp7xAkBU\n" +"ZET9IvJLXjhZISItUXbVHIeNUIqC9sBaMbKx9EGioF97a2gliT2O7cgRtuPM+ODq\n" +"ETHILlNc5G3vuNRBt4x3AkBV98Y1SZA3TQlUVTsjGraxkFTfU1IlomiOdOwTQ+xZ\n" +"x+JxhhgZwZ+kgI3PidEufFCTZJ3WO6Wk9gk18Bx7CLjm\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXgIBAAKBgQDq/K7wNW3fcTbaRTjNZlM4W0G7tKeO+X0bca4+9uin3ML3ogNJ\n" +"6qT/B0QAZB6Vyi9kKa3E8plQkjmPuX8Q27zj2QjEuDZ12RGFnikeOosUhOYiDh3Z\n" +"T9CHnr6stozzgk79Xd6VI7bqRcgRwbY0uc9QVr6vwddyIfSploSpVcgspQIDAQAB\n" +"AoGBAJfUpo/sZc6uzxtfCKGmkPTj+ef3hSBbUZuu60AhtxfnC06HrwpOg0eJAUYj\n" +"aqOsHMziJTYQ7kDiCjE0UMaqxDNS5hueumznq2xM2mSN0nYoktU00kpANVkW4VPA\n" +"33TB16DyqlKq2/21Rs1g8/8+IKkKDbRLTC//1WqNHASQVoGNAkEA/+z4hxTVXZkr\n" +"9hz29tAHKURlqzxUEKLnS0eL+XGJRNfGJ+65eXL+gFiIbTnpVeidL1+lKWkZyYzl\n" +"75cNRdUHhwJBAOsOJ9mUOqTbLW5tzh18ewZGOa1JcxhOvf2E1d56N8tDK6lvoqkF\n" +"oUUb8kIweDxPLCVLCl8qFrbjn619fxDInXMCQAfEZGKNIlCd5nSoumIRPDZnagKB\n" +"aTe8CfMB7+CZLoZVWiE6IIzsDYdNqI5QFKHT1nlqmLOiCfNRAGV+GxwEdB8CQQDE\n" +"sHu4HclU2fMSTOAE3H01qt3om2WsGXfyBI3SNQMrG3IVvkymkwd4BQKbUGPMU5Pl\n" +"QP3U1CtdruuXCUSijrzxAkEAoqYub6+0zM8fakSQZcZ01TG9Fuo2xVFDCQsvqR3m\n" +"ZhRT/oinIvOxSh4fQs40bmt1RBmc2L1Is6YB2NTVQEBZDQ==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICWwIBAAKBgQCrf0rPvHYaGYQrc1ciRwaONs8TUvSVmUU98HMYXoFEkBL4CAGH\n" +"4oNHFk8kXHEOsBED0eccSYegWhqKHSz7PbjmJaXloExWrtx5ea3Twf8VTgcfDWQP\n" +"0TzD3G1TYjAFPQ1/LAZCpQFmwpMmTGGxegUhOzkpEWXdLVEVc9Uw4C4L2QIDAQAB\n" +"AoGAZXAJZA5pHM7y6nBynYe9TOkGWru6h7H8zsImkcd0VoWRcrvpi+JjG+0KKsuy\n" +"46kop0XEmWq0mhgxknfnX0QG1MKTqGMIUGN4qCaezOabIpCOdA4d/pr/mWoNgOWw\n" +"9Kc/tNCrKxPKsQMAlWP6ktHN30XRSlHgAjSeUVUiNHztvTECQQDUNin2nyIvj8ZA\n" +"QAsFW9qW+TiTkeUK6yiZ9Gvgf20gwZRWOe5/xnMxVvtN6v7Av1ew/l4VhBoj/w5g\n" +"ydIZk+2LAkEAzuJwdt+ccllG19qmEcbo9XFafgi2PvlEjPJmT1rHV2ns/7HIMu27\n" +"PJY36GgExSfFco6VmicaoOt+RKg+5acgqwJBAKQxAEjcGWQ5VsgRhTVxO3DChX7Q\n" +"TColhrWPwwPhM/s7K92HVzwvvKL5TNmdr9xMb7n3Ja56FouxZVuH6/J0XT8CQAat\n" +"Mhnz/3WFQg8HRGLAe5YoMVZt64u+uaKe1ARtlo9QoNBjqWVTXL6IzocWjEjcjrey\n" +"uEtARdC5qNqIX3dD3H8CP3pVCPvpHOTxkUaktmLYowSA1HSfO9wkE6bMCHhkLwXF\n" +"yTIJ+N7c5u5YN1B6hhVqpKbdnSv+K0MQ0xbfwOWNMw==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQDGQmrKfO3WovoXkOTSh/shO9qjbX4izhg4pccVU3Tp45v/dgAE\n" +"uDUuaa/clToyH5AhOtuazO/asC3ZNajg1ia5VPzmQU3gtqiIZIEXFaOovPlOrXru\n" +"wyQnxaGORndJwfDXicG6bUwI+PDpNq8c4VOTujReeF0r74qMSc7TQLVlUQIDAQAB\n" +"AoGAakR/aTm9YibJVohbnl00xoOGlcLCsXU2lmaFZ3DsYdGWdD+TkvQJzW7ozJtQ\n" +"Lj2sy6L4wujGR7nXWW3hr2IaLpoc1UoyJpieAZM5os6bMN+N4MCqdcZMlazMtSWV\n" +"UDO7O7xQGFpcvvZmnfKCyluFaJ5K/tWxP+2TnS1/m0BDRIECQQD5DYvToA0eKBt+\n" +"7K4eEI8pzDot9NlcL21D86kNgpmuY4pifALU7GvXr299JpFFiYa2A1JVRfpQaoI3\n" +"hZzz0ze1AkEAy8opWJP+T2q4reD5Qq5UjjrHUXFID23KeJEjh5YF40/bHqyVpWVR\n" +"UMntNgAzs+13vRij48Zn6I8GRhStaQ3ArQJASPyFS8GN1paeaDXoWPs1WWR2cF1f\n" +"DbsAZHeVxVXOv+J//ZimI8wdVpodLCoPTLee+NxEVqUpVEPCYY8QjgwKOQJAATmj\n" +"6f5pxvxzQ8hYd0gpBfngfOLbdgxI7VSiDAyg2G8AeDy9YZMsW/n6zRpPNUO2NpLR\n" +"WWs18LX7aaxyJnGIuQJBAPPfy9pd4XEFsRBIIe3N23Gua1XkS/407RJtAGm73Vrt\n" +"QhtWh3i6D5gfpEApMoaE8aaQQ7H0z+0Uh1t8SWesy10=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICWwIBAAKBgQCc/M/X8etUqrxnmH3PyuAYLIPZhwNySch8qz9NB47izYjxzuBG\n" +"GSls6H7WeKIrB8UJY1gW8TLkdOLcrI/0hTANNHEPaueOE0xdABFj7tAaiiGPIM25\n" +"N0wc76me0ZAMYJrZTHk8JZK153y9wInYBwVZreXCVSVf11RuVwe+iFQa5QIDAQAB\n" +"AoGAQC4XJtivdhDLL6snHFF7pkZkrQTGgu3pOhakrXA+mTigGQOTqvTUe8LdP/9X\n" +"hTIK+tiTheWcAcxLhx5BSB0/VDKjYhS0ROpTc33Iq9KalOQaTJbBYGA4eagpQjwU\n" +"jGwr9u2sUsM9WI/Jg0VvLSKhfnNwYIUzLpK3BbWb2qAdh+0CQQDQ2s/8DlibFSBK\n" +"UsFK7lLpV8UgMk9CkaNM2BPzI8Hsjpp6s3pULVRd36m4YTSg15EEHv7bZ1N/+krX\n" +"mXb9xUULAkEAwGy5wHsUSjTK+kntkNXjlCU/+9R+HFpzg9Bwm/PqXTBwEWeU24hV\n" +"iRjPvqPtWFZrWi/nfcviuMaqtdliw1I1zwJAZ2mQxhtMYC2LuYFUWAe9YfClmJWQ\n" +"jUOTef8bka5I3RqW/t5TWc7AEWMnpDXtWx6hnUrDolt9Cschu7MvKeQ9lQJAL18U\n" +"46PpPNN+XNuyVoOxgRkihVasrUI/SeYYsuv7eHGiRUagyOLpW9T139LvbV3pE8zT\n" +"So7VA/Q0towL2lX01QJAGcoBNNouSpum9+5NvGQK1XXsZweawE+pFR2BE5XcjG+n\n" +"FnaLEUBX7nTxhTU2cSQET1PKRNp568a281NEna0nxw==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXAIBAAKBgQDFOqqGG/VtIScxayZYZ+BT+hcs5W1bD5qRxunbG9O36UVT18UE\n" +"CWw9HUf0Q5sDMGvVmBxwZ4GjbR5FDPfhIXaRCzobnejJXq/0k+O5NAVkcSPtJvhK\n" +"AaUqBrWA41vnjKOtJudTsZLfufKafzYwVonze7fXGyVsBRjVwHNS4iqq2QIDAQAB\n" +"AoGAJCoStI6R3RXUKvKb0GATuTJFZ50WBTmCPTK9FMkwdCuY47vPy2Ky7y3cUMTI\n" +"urf5PewrYs0H72CFyWGMXkKVi8aOYshsATEXMfGSqOcqXn+UDssRzvabZFlpnAUa\n" +"WDVt/iN092AdakXNna7/DxrLisDpq8HHJfjtlWGPfkXRg4ECQQDpHeKimTvwJcPc\n" +"iDa6Qb/n9gwLeRckfzhYtfX1luJYLIOHh+J9vjQN75thenBLQB/B6qlKtOn9ejxg\n" +"5z+3zIOpAkEA2JbxXVTCOA802p9khvHxDtLHdKi3w/BjjJiC7Mgqo69ZI+s3PB9E\n" +"F2HJA69kZqpGqvybWHDapjWsq7rcMlxrsQJBAME2yvR3y00VEAyGPc4M1vF8ZqlP\n" +"uRW/+ETWtEDUyU/JvU6lGt2bu2tdkEyv/cjxIiFIzP4litdT7B1pLc+6S9kCQBwE\n" +"usiWFGHoJbA6emiyl7qRLdg7kzo3uMkRWa6D3nA6WM+6t/SBHu/faH+fit91G5s2\n" +"/mmcf8yMmP/GNoIVTqECQFl4Pt6yGiz/YVoYSp35ljY5n3JB6T8o2pOmIrRLuPmT\n" +"6kgyygtJBAmx5nnQoeG8n08tl9QakWznKzkNJ0DIFKI=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQDCaOqJ0lsSAEBcnNB6X7BvVcEcol+evi/nJsPe0uT1SbtW50Ch\n" +"vYOHwK6aQR2C5x9VSs47cLynTL7tNt5d8oeryF3NpI8VTPLImDJCcvUZhS7p4bxn\n" +"JO+Wm+D/e3TWfyjreuWtdL+Mfimw2gzwWuBEtmj51GzQ89eYm7fh11SB6QIDAQAB\n" +"AoGAWaakMbZNxPlUtOCjyysBY/Y5vYira7rswD3CKak7aFn+CE9QIMYSN7IFUqEg\n" +"iNMoQd7jR8nvVX8wtJeO5+gF48W13C3n8FZSrW7c5N3bmfMIgo0xa/TGfeXHP98o\n" +"7vhH0I58j3ZZt0Q+3wTm7t7WPE/nJzgrCk30TqmoaEmstTkCQQDtV6YZ6juEK2Lp\n" +"LGUiqohcS/WJxvFrF5+LNpk86Xdgomf6FphZlkq42KYkvl7qibKDcfDqLKTbHHle\n" +"vQQeCgZ7AkEA0bFHi7F8o4iHtKleBvt4QCj1neA0q3CRDypCI5EqFSrNpxY4Krhh\n" +"WYSVX+xT00QYaCpKKWfYQztCw7Anylv96wJACl86Mwe5ch0zRV1bThiFvQLUyCCZ\n" +"jESMBFlueOr6/I4cXSF/puqaeVl+aTyoiTdbRcNE8/bffXPRGgLIm0d04QJBAJSY\n" +"lmTN789Lby99Xh6AkaSV4ghw26Ip8QHYJmph8npxjK69Niw/4Oy44cnKBVUPSmR2\n" +"o3tYFY7/Lb7S1D+4lOUCQQDbMQUGVsZT+ZjuOG1bAjIuXoAOfOd3mgH5VgQHjSgJ\n" +"ourZtlJ4OUpNrq9IfWqPkM+zSE8+0Dk8/9MS5ngBA/SJ\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIICXQIBAAKBgQDNbHjwg+7tVNr9erMLowXRnIcttp4pUJbr3B7Jo/u+kD/Yo3F3\n" +"4rIKhHpJl1uEHP1QmvAD+4ApFFI2hNG54xYI8dGflxL5HOs5xxyOPpkrwzQ8Qvnv\n" +"LPg7Gf6PAW9zF4McG4wK0TkrV28G6NhqcPs5VFY6UyvfZ0fEdWAeoWTIfQIDAQAB\n" +"AoGBAKOmkMp7MLLd8QAS6eSRYSdWHdLrMyES1MjduaFGBF4SKOr7en/Zl6ENXSaX\n" +"cA7V0XCPnjpt9/HCAKTyNupx4LCeFWiqdu8VGXhlzX8bdb896OSR2brKbxgRY5tF\n" +"36uL8akrZdrYgocykQCxmRARMB7/rHwDusiamjL6RUZ3+c45AkEA6UPTVmKZQRMr\n" +"A7Qgg5nXrXo9117Lpqf3FdZ1wdni9V59Ptf5xrx9oGZNZzctJPXSAH4M4cumSJrV\n" +"sZ1V8qE7AwJBAOFx+5luLrVKrdlG7MyOhTAdhKYUvKIvL4wvVSY6y+L2nNEx/cTx\n" +"KYbxGC+H1RJbkCS09rYir3VfDRWQ3W1c1n8CQH+X4hn2hO3blkPIW6CgniD+JKWR\n" +"7MOUTMtdK7yFemfM76VYbgAPSohabSxwOfllnSE30cQQqTw9tXYaIdE98BECQG+M\n" +"QWxSS0QillB6unIgVqBPCrJOcmNhK4qWZPBMiVNcqI0Nyj2nAeAl7MyfzfqOWY0A\n" +"CU5nbR+LD2NLUXRqSisCQQCN3IGv1WOWInmA5xhU6vCFDX5u48Dcji7VLJO/Nv/i\n" +"b/zHKAgjHk5Js7bi5ZWEGaUgA4Jt6cKmGdERheqTMKxx\n" +"-----END RSA PRIVATE KEY-----\n" +}; + +static const char *PREGEN_KEYS_2048[] = { +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEpAIBAAKCAQEAoksI1qIuIaFCqT4QbgDvOQCmr9Z9F0E7ku+U5Ep/5dWNANqB\n" +"bSzAOq0+cxiisfF+H4desoqiWDUwlOwXH74qD3ZsbChhvFUD78cQBWQkF+whLVHb\n" +"296QmF0LZqosqz9HMS9CdoMUc1brZb78Hb25QIOOjrg25KYHLZHaqcet1wfhHow6\n" +"Uehc6QTuWgOWFhJnfiXzYgen2o8lnLixxZozhk7Lm7Aix9ur2ckXdQ2Wgny4xw70\n" +"JW84Hapnd8oFUD98XXrExk4VFuIcA8qo7r7y18II6wx4Cw1suKru6bhW65cM/y51\n" +"KC4lB7VkvuoJCelRFdM1PfKZLv2tJP63oAqJrQIDAQABAoIBAQCWc38PEqw3avqU\n" +"UMAEaoNa0bq1Gd8/Nq8WqVnbRSFKHO2pk+cWIb1W6BITuwvgcGKesezdEV4s7apK\n" +"9I7/U1hEm2Ep50mrwRh0KZM1nD9Fmharn851Bt//D4qpMytT2caS1yADI8NKpZJ1\n" +"8VZh7+cT4qG+txHUaAIRgbw3VrBWvTIMu6SOSOZm+e3eOr5UU3du1KvjdJHJ2c2k\n" +"TceHvUdKxV7OYt+BBSN1oBOhs3ajUSRge1v3twRDg3cmbwG0DeXvwHNhGUTcF8IH\n" +"JO1RF5njbkFvyqdAi3ltjU41zYd4OMuPtrwzFOtxUjKT62Soz109HUXXE2CGKFPZ\n" +"PVi5/BIhAoGBANN1xqS5BgHszIB0nXbw5ImYpTRmyhO0KsTblBT9+8Q/B7BCK7bM\n" +"zl+dOPeyvEadSwE7RSMMt6CAlTakWIf3Quw/VZajvXy9C9/LHf52pEKXjxMFMPKE\n" +"aGLHpQnwMtDi8/H8AEAXxI3hpxB2KVR7sAYHWihSGjRJ6oPGvEmKEkb5AoGBAMR6\n" +"G2PKz0xk1vFrjfjSY+y13gH/t7xHaXUggjggUSGKaknQh2BDUllXjadeI0fi1eLW\n" +"r98ZImZZgntAgjaIZ4bAlooTDk4gRHaz9jI+z8lsRwOKnWdiigM7txiXZTMVwMqj\n" +"o5mMNGMA+A+ACkTViRHmkDI7S/9FqAvnbOqVwgFVAoGBALUcY6WDvwx5B3Jh7tgH\n" +"XIYpEh3+h8c2gYcX1g3gtvkPTwN8uToY0gz8eOVV1YHZiHsmi4GIi+HRH3usaRMT\n" +"COOVHzYlSc8Dj57+tdLTRL6wVl9hC9o647ju64DGlI9qQquYPZKniLZIdbFYsu9j\n" +"/JA9Tc/I+h6czFpPJccKlbrpAoGAAPWXrKUQ3g6f/g3IY66jTkSVEO1uuDyhBzFh\n" +"cWS3ALLsUe/yuUWa4VTMHEUZZwB0iucBdNVqlZVaTb/C4wFHgCDwmzv8leUScIHw\n" +"cc5ctV8R+bJzkk2o3tsrybLzi4xPpK2n3tgQaWtXyruVUUC5qpy1l4kylcyBRY2b\n" +"uomAqQECgYAiCNWtuWIDlRBcvtIB+kHguzcoFT3vTCCNhalTEn0zi/tbi+voQgVJ\n" +"SDJNptZv+6vRwQ/HfcQtljKIPO6hUZPYaFWRNhgbh7Ay85lRXYXQOottE8ayReBk\n" +"zZb0fl853Qah4DPsaOugAvhjjKeBmKg6bFWO1z6hj18I3UpDf2YnVQ==\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEpQIBAAKCAQEAssO0r37mSJNAkc/ISwXBsu9JjyLeWlsHPAhylQGkSAdp2rjz\n" +"E6AT0Eh3wrocNO31I4pvHReAuh1QedGY6T1cQwO/WAAhQtRCBQDK12qWRgfbC11y\n" +"Xu7zNYPd1Z7YIRy+FxhbL5f+lv3rEUv0HUG5c3CWhLtbANKg+jOieIDzA4Yp1s55\n" +"ynodQBUkTZrwQiT0P8yDSjiasf+clgJRfA1k2XK12KSAMRgyDuPTE4OtBxBvUM3L\n" +"Zvxs81PsmcOuAG4DLaFTg2a/QkCjt2VC1SYYuh/LVxpL41FFh3eMoK5g5deHkgRe\n" +"tlywKjAHIDJu/qgNzNgNW7ymwn2CfBvry9h0/wIDAQABAoIBAEMZ4wDdCWPEokAZ\n" +"Vn2Ss5qO53WrCPuxn42RPjFgZGIFJl7LfbKoK8fK6+lUIrJbf+DPXdX1tIQn7MVN\n" +"P7CNL8yX44MMyW9kbUOjgIBLqgyvdjFV6lBoMTKtRN+iuE31lATnR5Md4pqaxVnA\n" +"wOkaepoycM1x5j7w0SwZparF/HIdkYv0y/MysqT9ByupPA4Fqp/iRSrosHXahNtI\n" +"KZYj1TyERYtuDXq91P4dr/pWq3FmDNI8O3upblkL0YouvG/ZlFLdiNy77XbAyWcX\n" +"ps3YDddM+vECnXO3+sa3ZxgBYvXJdWrrIzM5A+jCkDRZQGsFAzK5I5/S7C2ljt6i\n" +"SmzqvMECgYEA16bGy2XTi6KBPb8aev/OBgK9XuGLwUqK1m15mS9Y2qPHmuc22qaZ\n" +"hw6zginPFrxAEtQWKanhZy4aVqlLkDPLwRnyeuMo1EZAc5B1gZ5ViSAKxBq99hA9\n" +"eqyakdb+IUQsEnRDxSc2gqUQ0EagksUyw5wGG5Q/CVEALmS/r1SU3KUCgYEA1DYf\n" +"6JYdzuRtule3vYeWXKf8sOJpdplgWV7tvLrKkQhdE564uwMCYB23HvYfwWqEdDYG\n" +"fsYg/ur/stk9MDZ3wZKffTEM8V3sX1t1JXnC3ogSAgMGhLZ3ILOLqkoO4BEZJnsS\n" +"dMdiNijlAtQkqs/BO/UVUAKysCtKP3v/+1775dMCgYEAvLjGFjApfnSbV/cK7IM6\n" +"wEXbhdIqZOCgOeEaXjVyM/zKbMRVW+oaR3hVHd8KzSG3jQKv1oxFpu9Qu3ByoWLC\n" +"uF3Ft0debs6ADuJoAyQWROeWpGGmxlUWCGpO5rxYL7KiQxAeUsXrTU+5NBvq4CbV\n" +"MxwyuCX3OGb7mp4upfiGQcUCgYEAuhVsDYv1P4LXJVvd5viKRV2ZG5KuYC1Ga5fu\n" +"aFxzXJI07At2eaa94oKsHR494mEBHNZzA5/BN0fiSHZuTWS1xqxH5oOokc6Gg2ez\n" +"ZdVLp88x20nD4YQPGkHW6tBeEuVrZG7vVC+yU0Ow7bYRISdkjqrusWZsQkbzqI+X\n" +"fFliEbkCgYEAu8x+47M1ordbI7NmbBGyiyP0r7nMRCZ+KEvGeCNYracWmsnCNnfV\n" +"zR2UzmwtSainw3Ho8Jv/rWDC8RIDauyBRYEi2VqOnUzT2ca0iymQyLeBCudAQuio\n" +"drOu4JU8RzZ3Ad6V3DNFnaqmX/7GA9Pa2GI8NJMyb8p1GAGv7Gi8nxc=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEowIBAAKCAQEAt01S8JuEwWy/Hzb90yO2O7oGWq3GfvfDpFOF4OQnwG3kQ/BP\n" +"4MoPDCYHdqb3iI9aD3vykZA6Q8zpdfGwjm4+bHrgRdiSmZWv8NvRwuQ5Ji9xbiGn\n" +"hA1XwqH9hvgFTiy6tRvirWSJ7kzH3Q/bEGpCbHUQkwMog4v6yCNKNrjlwjN++eCi\n" +"gFK/0RMOJMLOs8BD3zY+lKjd/pd8LBRujkMyUF5SryeRueAFjD2sq4OXq8DPABGt\n" +"zdR6vbTcsi4JwP1Q6y4x0/LIWEprzzewNU63I5E2zj0WnoRGAIM4aF+VuqcHjWUx\n" +"VWnyLZldSen6lScZ4xj4seitiDbSFvtFkDF6VwIDAQABAoIBAGTP9im2ntDyyjqU\n" +"uA0DuxomOZBtupniEouyFBOX5/UBe2WSKZxsBNKdp8UuFz3X+aRCeyprtF/NtyjT\n" +"AFOVdmebPPWtIxOtK9LAUyFo+7VwqmXzxHnwDLBS/2jXx7MzDozFBWpvvRx+xf1i\n" +"1wy0JEwaJj90oTeYKRkhr5NhJZwkX8zCNYaemBd3kHB3aGWGJasI1Y81UezeRKCn\n" +"hSbn2CrWalI7pyJ4lsavM11nIq1Eu2ZthJiNCMghbYrHoBHd+iVWiCYchP2rNEWV\n" +"sdHtaVHtQ9zdZ43bao3OzPu7lAjd6UAbxsuhUe+a2YdDz/+Up+6+BvQf1FCfYIjW\n" +"KFUdCoECgYEA4t5O+u0V9gkMUhKsevYb0zgc7O/mo8ivN+V++EpAtL0mhiwxeO8p\n" +"oef0szLyhdULQeLN9pJQDCeAbkGdwIe3L+AKU8o8BFGEWLFysZjMg9In/UTrp5MN\n" +"mMDy2SRKKu5BqsvdYH302xpZfHq1T2cMNDWE8lrZffduH06Cgq/XEtECgYEAztbj\n" +"bhFneADnrvk609VnOQvoQEjySeCQKFQFRRI6k/FguqMisL2IRXnMaWammosdeCAg\n" +"m7eZchnszHIst9cwZUKXUFqmAqeDuWSNdTI7uKZH6nT/A6IDlgdjaHsqhvpK0Ac9\n" +"ngycdHONitOZh0ZG74pdWjf828Dwzf+CuYjl9KcCgYEAmIvI6ZqvkJ8m5Kzfw1Jn\n" +"BVCOypbJK8oOX3R2Orea6KzjEYb3wQx3nwFcHX6danYFOskpmqlpH7MT/Y8rZsEa\n" +"4RsxdoPedTzm08iFiXtn0R9nejp0hlov402iPXXUVSedih3IflBTa1w9XaEY9wog\n" +"P57ZBSknYzcTmgNtaDiaUnECgYA5sWauhNw/dMEq5QmrnJK2LsQRakdqo+CR3x25\n" +"LmR4b5Nze51pfvRLrLV/kMpXwQXvQ8bUqFl8og6S2CXxAWzWUcSy/RXhF6h+RbXP\n" +"Qru1vWvB0fBvqvklF9p6giBSle3YKKzfMNVTBggs+OiR+uA+YHG5gHRfN2nzi5mC\n" +"9tRtcQKBgBnDSi4lRCjRe9pPnyAYaa4iyBUGhjPysScSLY9orel89+qmTBQ/Py6J\n" +"0+sefL4ZJaOsuaR2mSSPP/lbSkF9DMFs4tHbBqY+WkVNYLshAkauHwqv26HTVCSd\n" +"QKzeb7uZw9lNaRIzDvy/3wfCLvXfdDozPFrOUgkyaBN5pJSA/4sv\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEogIBAAKCAQEA9qtiDoJWqU/eSlpj381eG6UcDzfMguFh/q4e4s7QVdRYj5J0\n" +"Msv0PCkti8JHuvQUyncRpOPccBkhNbVjNbjIgw1pHaIZNdVotUDhP0kseRyJ6z3M\n" +"qbZ5qKn+0mHjVjPNItVDDe6tebYMT1BZpVyRrCOqY2v5z1ecLC+ReygmHgDpzg+L\n" +"0rWfIxGT10IPZ8pAlcdEn6xt5aEhi7mPCX/xwqfQChPIJz6zVLEC8UaPtvDBohPR\n" +"6NQTBTeZZAAtzrQ7+oNxfz1v6Fz6RwMei7Q+qOBnMiwpQmbcDBKABM2RnXSpD0LA\n" +"1GR7/+CiV1HQoShWVvEwrSIlM6jVAJo6iqF6WQIDAQABAoIBAHqwcdxPnfUm4aTP\n" +"4r9NcZKEhDlZgqJSoiA/0OL1BRC7xrTanmspoLhPrvTF1FG715+Aq8j9AQbMqQUC\n" +"zG7LEwiEIhV4K9vn4uXMeHy206UFud/E5EhBl695pmJUB/Q3XcAGnQyP+77++o50\n" +"o7IpIdeiAbzj1uP3aplbq5u7M4JV7fUZWA/368G4HolqFTxcAfBJ05GXlp97BBwY\n" +"AnY3/pNrKMz0NiPf3nsJHYWK18up0JCLPL3tomc94wuNZ66spIazHIL9aaKY0q3V\n" +"LkBrelndfYM1m4xRTnSOy6STu0qKTPOpX0C8XBLYs6uiXjRsChqSYwndCCeASaH3\n" +"LGNIcbUCgYEA/m4qvt8tdT4wEvnE+QUxEELmBtT4UFa3NnQISrzNlhNeI0Zd2xlp\n" +"SG0/pcw83mG2uX+V5xSaWL5LYfLBkvy83Y0yIWgYbbIkyyCOUZnTpwaDGU/FjWip\n" +"3TfXf5qpAgiez94sV+MsFpKfG05yxJh5u+3sIyGTVUAxp0HPx4LVgbMCgYEA+DD1\n" +"fu6ttpuV1UMrsFdjuk6gBvSbyJ9OilY2jT+yE7hSRc/yP3O9ikuR74tNlVrWTnO2\n" +"0kcYbyLJXE2cGUC2q5e4r8TDGiozNfQ7/OC2M3XaJ+xJk4zMf/8PuDDpWr+18ZXA\n" +"Pf+ibXWTFvZ6ZeUmpbrrfCrXdvmIZnwVuOI0FcMCgYAZn26emksxq3mb75tumJ9A\n" +"S/xuY7Q+Iv2Adl7/Z9QscPbiBowdLIn1yUrHn7Hhk2WbeMXX57NDjKZ6zr+/1cQP\n" +"a9DInHsZUP9zlWu/vAYcpAM/4VC71PaGWMFTEHhExCl6NZ2xnCcsfseXMGdOdSyN\n" +"SICnaRI1W6mkdnQ+W2a1EQKBgGEKA3KVr6XuPy8bDEHuaTe29irCCQbwAq1j+ABS\n" +"HzZGoyRYocbdYgZoda7LMJJs6c3SwHCHC66oU0KbtaTKAKImuDdBH2djiJJX4/yD\n" +"f7mvIpTpdfsS2gJRn7vMo/CvdFv4ySl0gfV6OwCHbmPYrLuv0dLCjWwfNI2dhoC7\n" +"MNIxAoGAIPSIG4BrShzbeX4c2L18iwIg+NlOcUbtl0Ccr1t6uLGI+ge/6I6T/5XH\n" +"DPKqYIf0IRYV8suxpfQNKiz/C0NPffA1d1M2hvuAg2v09o2cSwvdcQwdmakKZ5bl\n" +"sdCuYKdCIwomEUOz/4XgQrJl4XDUqxftJT6/egAjWvcIYvfNCsY=\n" +"-----END RSA PRIVATE KEY-----\n", + +"-----BEGIN RSA PRIVATE KEY-----\n" +"MIIEowIBAAKCAQEA1yHZMsgRLckL+v6rgpGq9qmxVBNDxeuul1V/QlFyOlcAk5n/\n" +"uduTalSqGQhc4NEePMxq6nFui4ucpkZOozmcEnhV0N9jld9IB9rLGt4erdg7RKl9\n" +"+gQ+zTn69j69U36E2I47H4dM69uxeSOyWP2Odxpw+biisa3o8mMz1zCmuj4GMDtG\n" +"DlnSpthFzgQR6N1pbvxLXrWg5F16GqFiJOD7kXDfy4/l6kB/mDs1T/3r8kav6DqR\n" +"c/t3aQZxgWGIpI7hc9Qgvp7coZRMey5dNOZEna3tqS8dn2tZlhkpYV5uyFUjmxjG\n" +"TERSULQ7hvUqW+eshGGsnxFtL7ANnTSc4xECowIDAQABAoIBAFhJJMhpQFuIySjd\n" +"AGeZ/g4x/3rgWQzNNp4WUR5XLEhy0eLA7ShJywp06kVRoEQGraEHxsyldldAGS5H\n" +"ZhgoGTufNKB+PHER646FpJpHE1IGjfQUloVW3qr8I1iQ0MOGBWCVpf+/V7rnMsLi\n" +"+lr421FXgYuJ0QKXuyRVv72M0q9U6i+ml3aVAhgW/19oFg+dW7YccX+9iVyD05Q5\n" +"KR64tX8xd4wrAqfAgYA3erbbE6GTyHYD5K54kIgfRr/+pIU4qc1L7XOCblnqc/rI\n" +"BilFysEC634r2MNe66uQvNui4oQTfBcFFlXg0zAmp7d5QE0ApOL6HpCsmbImm2uJ\n" +"sdFNYyECgYEA716kfEv7HfnF0P3pAP2AOuEsW6t8q0UtWvnHrwRQXQw8Yv90g7kD\n" +"pUV3/BjD9VQgsQZosbdSn5wbT4j7dypRdrzYk+8m/hBk4Q8M/tWoRGVOn46NudvK\n" +"/KX0A4ODLuulj8yAZVc7CM5Cdy4GCGJBVO+oVvBUAnHxfZziOyqBw9MCgYEA5hQg\n" +"HEORzdxvbbfAx1ggvH1Eg1lqRhmpI43PpRkaoqb8jLwXb2CyBeuv3RBft/X2Tr6F\n" +"mHpe0U1kN/5YEjii/Q/jUX8azIHaUNNSAjrriEeMQZOqFxmhCdiyeXuqg2fbFbhe\n" +"K3Q6/fsB1xj9OOSwyPMqm/M5U0LsoGjmg8TFE/ECgYAlImKUIdlwOgp1NJ7MF4eo\n" +"Gryd8AmkLFQv8+YFgb7R4I8RsJ2rva0SG6fUhScJTSbRL7RYNZ9swXP/L7oLL5Z5\n" +"vCxBLu22pmZv/7y9X/n9ulWrLRtRhQaFkV08mk9knQwPNeOJVTIEWLM49/vZmxyV\n" +"h6Ru8FOoGXMkUI1MLnj5HwKBgGJLkNhiacVYeuaWDa9c0EeXARFYvxWJ2wAMkvzG\n" +"9+ErlFQP+7ciyYvMAItidnJii8NilDLrfNzQwpNFf5zxQ3j4M7bapblfdMT5M10u\n" +"jPfhEWPm0VEjKvDI+p76HYQcd7YU2W6ZLqbZeRTLYUvQMFL5yGduBzyyJ+P0TR9Y\n" +"jpYRAoGBAM7vYGTprw4w2tTZPFICXVk1bQ0LO06oNRtwkiQTUT6UqPjWMFyvHnmN\n" +"11SVVBmRZ0RAk6e5eZLFX8WelJ4J4nSOGRcJheCtoEFlO7D1ewAUSbqWJ0pBqp2T\n" +"gV4oCS8LYe8zReVoYZJjuLwoHvxZzs/hUjc3SI2HRW2W/HQRPC25\n" +"-----END RSA PRIVATE KEY-----\n" +}; + +#define N_PREGEN_KEYS_1024 ARRAY_LENGTH(PREGEN_KEYS_1024) +static crypto_pk_t *pregen_keys_1024[N_PREGEN_KEYS_1024]; +static int next_key_idx_1024; +#define N_PREGEN_KEYS_2048 ARRAY_LENGTH(PREGEN_KEYS_2048) +static crypto_pk_t *pregen_keys_2048[N_PREGEN_KEYS_2048]; +static int next_key_idx_2048; +#endif + +/** Generate and return a new keypair for use in unit tests. If we're using + * the key cache optimization, we might reuse keys. "idx" is ignored. + * Our only guarantee is that we won't reuse a key till this function has been + * called several times. The order in which keys are returned is slightly + * randomized, so that tests that depend on a particular order will not be + * reliable. */ +static crypto_pk_t * +pk_generate_internal(int bits) +{ + tor_assert(bits == 2048 || bits == 1024); + +#ifdef USE_PREGENERATED_RSA_KEYS + int *idxp; + int n_pregen; + crypto_pk_t **pregen_array; + if (bits == 2048) { + idxp = &next_key_idx_2048; + n_pregen = N_PREGEN_KEYS_2048; + pregen_array = pregen_keys_2048; + } else { + idxp = &next_key_idx_1024; + n_pregen = N_PREGEN_KEYS_1024; + pregen_array = pregen_keys_1024; + } + /* Either skip 1 or 2 keys. */ + *idxp += crypto_rand_int_range(1,3); + *idxp %= n_pregen; + return crypto_pk_dup_key(pregen_array[*idxp]); +#else + crypto_pk_t *result; + int res; + result = crypto_pk_new(); + res = crypto_pk_generate_key_with_bits__real(result, bits); + tor_assert(!res); + return result; +#endif +} + +crypto_pk_t * +pk_generate(int idx) +{ + (void) idx; + return pk_generate_internal(1024); +} + +#ifdef USE_PREGENERATED_RSA_KEYS +static int +crypto_pk_generate_key_with_bits__get_cached(crypto_pk_t *env, int bits) +{ + if (bits == 1024 || bits == 2048) { + crypto_pk_t *newkey = pk_generate_internal(bits); + crypto_pk_assign_(env, newkey); + crypto_pk_free(newkey); + } else { + return crypto_pk_generate_key_with_bits__real(env, bits); + } + return 0; +} +#endif + +/** Free all storage used for the cached key optimization. */ +void +free_pregenerated_keys(void) +{ +#ifdef USE_PREGENERATED_RSA_KEYS + unsigned idx; + for (idx = 0; idx < N_PREGEN_KEYS_1024; ++idx) { + if (pregen_keys_1024[idx]) { + crypto_pk_free(pregen_keys_1024[idx]); + pregen_keys_1024[idx] = NULL; + } + } + for (idx = 0; idx < N_PREGEN_KEYS_2048; ++idx) { + if (pregen_keys_2048[idx]) { + crypto_pk_free(pregen_keys_2048[idx]); + pregen_keys_2048[idx] = NULL; + } + } +#endif +} + +void +init_pregenerated_keys(void) +{ +#ifdef USE_PREGENERATED_RSA_KEYS + const char *s; + crypto_pk_t *pk; + unsigned i; + for (i = 0; i < N_PREGEN_KEYS_1024; ++i) { + pk = pregen_keys_1024[i] = crypto_pk_new(); + s = PREGEN_KEYS_1024[i]; + int r = crypto_pk_read_private_key_from_string(pk, s, strlen(s)); + tor_assert(r == 0); + } + for (i = 0; i < N_PREGEN_KEYS_2048; ++i) { + pk = pregen_keys_2048[i] = crypto_pk_new(); + s = PREGEN_KEYS_2048[i]; + int r = crypto_pk_read_private_key_from_string(pk, s, strlen(s)); + tor_assert(r == 0); + } + + MOCK(crypto_pk_generate_key_with_bits, + crypto_pk_generate_key_with_bits__get_cached); +#endif +} + |