summaryrefslogtreecommitdiff
path: root/src/feature/hs_common
diff options
context:
space:
mode:
authorNick Mathewson <nickm@torproject.org>2018-07-05 16:31:38 -0400
committerNick Mathewson <nickm@torproject.org>2018-07-05 17:15:50 -0400
commit63b4ea22af8e8314dd718f02046de5f4b91edf9d (patch)
treeaf52b6fba37f22c86447fd5267dd5eb557807c8b /src/feature/hs_common
parentce84200542f48a92e8b56a8d032401ecd153e90c (diff)
downloadtor-63b4ea22af8e8314dd718f02046de5f4b91edf9d.tar.gz
tor-63b4ea22af8e8314dd718f02046de5f4b91edf9d.zip
Move literally everything out of src/or
This commit won't build yet -- it just puts everything in a slightly more logical place. The reasoning here is that "src/core" will hold the stuff that every (or nearly every) tor instance will need in order to do onion routing. Other features (including some necessary ones) will live in "src/feature". The "src/app" directory will hold the stuff needed to have Tor be an application you can actually run. This commit DOES NOT refactor the former contents of src/or into a logical set of acyclic libraries, or change any code at all. That will have to come in the future. We will continue to move things around and split them in the future, but I hope this lays a reasonable groundwork for doing so.
Diffstat (limited to 'src/feature/hs_common')
-rw-r--r--src/feature/hs_common/replaycache.c209
-rw-r--r--src/feature/hs_common/replaycache.h67
-rw-r--r--src/feature/hs_common/shared_random_client.c260
-rw-r--r--src/feature/hs_common/shared_random_client.h47
4 files changed, 583 insertions, 0 deletions
diff --git a/src/feature/hs_common/replaycache.c b/src/feature/hs_common/replaycache.c
new file mode 100644
index 0000000000..b5cc6a2823
--- /dev/null
+++ b/src/feature/hs_common/replaycache.c
@@ -0,0 +1,209 @@
+ /* Copyright (c) 2012-2018, The Tor Project, Inc. */
+ /* See LICENSE for licensing information */
+
+/**
+ * \file replaycache.c
+ *
+ * \brief Self-scrubbing replay cache for rendservice.c
+ *
+ * To prevent replay attacks, hidden services need to recognize INTRODUCE2
+ * cells that they've already seen, and drop them. If they didn't, then
+ * sending the same INTRODUCE2 cell over and over would force the hidden
+ * service to make a huge number of circuits to the same rendezvous
+ * point, aiding traffic analysis.
+ *
+ * (It's not that simple, actually. We only check for replays in the
+ * RSA-encrypted portion of the handshake, since the rest of the handshake is
+ * malleable.)
+ *
+ * This module is used from rendservice.c.
+ */
+
+#define REPLAYCACHE_PRIVATE
+
+#include "or/or.h"
+#include "or/replaycache.h"
+
+/** Free the replaycache r and all of its entries.
+ */
+void
+replaycache_free_(replaycache_t *r)
+{
+ if (!r) {
+ log_info(LD_BUG, "replaycache_free() called on NULL");
+ return;
+ }
+
+ if (r->digests_seen) digest256map_free(r->digests_seen, tor_free_);
+
+ tor_free(r);
+}
+
+/** Allocate a new, empty replay detection cache, where horizon is the time
+ * for entries to age out and interval is the time after which the cache
+ * should be scrubbed for old entries.
+ */
+replaycache_t *
+replaycache_new(time_t horizon, time_t interval)
+{
+ replaycache_t *r = NULL;
+
+ if (horizon < 0) {
+ log_info(LD_BUG, "replaycache_new() called with negative"
+ " horizon parameter");
+ goto err;
+ }
+
+ if (interval < 0) {
+ log_info(LD_BUG, "replaycache_new() called with negative interval"
+ " parameter");
+ interval = 0;
+ }
+
+ r = tor_malloc(sizeof(*r));
+ r->scrub_interval = interval;
+ r->scrubbed = 0;
+ r->horizon = horizon;
+ r->digests_seen = digest256map_new();
+
+ err:
+ return r;
+}
+
+/** See documentation for replaycache_add_and_test().
+ */
+STATIC int
+replaycache_add_and_test_internal(
+ time_t present, replaycache_t *r, const void *data, size_t len,
+ time_t *elapsed)
+{
+ int rv = 0;
+ uint8_t digest[DIGEST256_LEN];
+ time_t *access_time;
+
+ /* sanity check */
+ if (present <= 0 || !r || !data || len == 0) {
+ log_info(LD_BUG, "replaycache_add_and_test_internal() called with stupid"
+ " parameters; please fix this.");
+ goto done;
+ }
+
+ /* compute digest */
+ crypto_digest256((char *)digest, (const char *)data, len, DIGEST_SHA256);
+
+ /* check map */
+ access_time = digest256map_get(r->digests_seen, digest);
+
+ /* seen before? */
+ if (access_time != NULL) {
+ /*
+ * If it's far enough in the past, no hit. If the horizon is zero, we
+ * never expire.
+ */
+ if (*access_time >= present - r->horizon || r->horizon == 0) {
+ /* replay cache hit, return 1 */
+ rv = 1;
+ /* If we want to output an elapsed time, do so */
+ if (elapsed) {
+ if (present >= *access_time) {
+ *elapsed = present - *access_time;
+ } else {
+ /* We shouldn't really be seeing hits from the future, but... */
+ *elapsed = 0;
+ }
+ }
+ }
+ /*
+ * If it's ahead of the cached time, update
+ */
+ if (*access_time < present) {
+ *access_time = present;
+ }
+ } else {
+ /* No, so no hit and update the digest map with the current time */
+ access_time = tor_malloc(sizeof(*access_time));
+ *access_time = present;
+ digest256map_set(r->digests_seen, digest, access_time);
+ }
+
+ /* now scrub the cache if it's time */
+ replaycache_scrub_if_needed_internal(present, r);
+
+ done:
+ return rv;
+}
+
+/** See documentation for replaycache_scrub_if_needed().
+ */
+STATIC void
+replaycache_scrub_if_needed_internal(time_t present, replaycache_t *r)
+{
+ digest256map_iter_t *itr = NULL;
+ const uint8_t *digest;
+ void *valp;
+ time_t *access_time;
+
+ /* sanity check */
+ if (!r || !(r->digests_seen)) {
+ log_info(LD_BUG, "replaycache_scrub_if_needed_internal() called with"
+ " stupid parameters; please fix this.");
+ return;
+ }
+
+ /* scrub time yet? (scrubbed == 0 indicates never scrubbed before) */
+ if (present - r->scrubbed < r->scrub_interval && r->scrubbed > 0) return;
+
+ /* if we're never expiring, don't bother scrubbing */
+ if (r->horizon == 0) return;
+
+ /* okay, scrub time */
+ itr = digest256map_iter_init(r->digests_seen);
+ while (!digest256map_iter_done(itr)) {
+ digest256map_iter_get(itr, &digest, &valp);
+ access_time = (time_t *)valp;
+ /* aged out yet? */
+ if (*access_time < present - r->horizon) {
+ /* Advance the iterator and remove this one */
+ itr = digest256map_iter_next_rmv(r->digests_seen, itr);
+ /* Free the value removed */
+ tor_free(access_time);
+ } else {
+ /* Just advance the iterator */
+ itr = digest256map_iter_next(r->digests_seen, itr);
+ }
+ }
+
+ /* update scrubbed timestamp */
+ if (present > r->scrubbed) r->scrubbed = present;
+}
+
+/** Test the buffer of length len point to by data against the replay cache r;
+ * the digest of the buffer will be added to the cache at the current time,
+ * and the function will return 1 if it was already seen within the cache's
+ * horizon, or 0 otherwise.
+ */
+int
+replaycache_add_and_test(replaycache_t *r, const void *data, size_t len)
+{
+ return replaycache_add_and_test_internal(time(NULL), r, data, len, NULL);
+}
+
+/** Like replaycache_add_and_test(), but if it's a hit also return the time
+ * elapsed since this digest was last seen.
+ */
+int
+replaycache_add_test_and_elapsed(
+ replaycache_t *r, const void *data, size_t len, time_t *elapsed)
+{
+ return replaycache_add_and_test_internal(time(NULL), r, data, len, elapsed);
+}
+
+/** Scrub aged entries out of r if sufficiently long has elapsed since r was
+ * last scrubbed.
+ */
+void
+replaycache_scrub_if_needed(replaycache_t *r)
+{
+ replaycache_scrub_if_needed_internal(time(NULL), r);
+}
+
diff --git a/src/feature/hs_common/replaycache.h b/src/feature/hs_common/replaycache.h
new file mode 100644
index 0000000000..3118a88a1a
--- /dev/null
+++ b/src/feature/hs_common/replaycache.h
@@ -0,0 +1,67 @@
+/* Copyright (c) 2012-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file replaycache.h
+ * \brief Header file for replaycache.c.
+ **/
+
+#ifndef TOR_REPLAYCACHE_H
+#define TOR_REPLAYCACHE_H
+
+typedef struct replaycache_t replaycache_t;
+
+#ifdef REPLAYCACHE_PRIVATE
+
+struct replaycache_t {
+ /* Scrub interval */
+ time_t scrub_interval;
+ /* Last scrubbed */
+ time_t scrubbed;
+ /*
+ * Horizon
+ * (don't return true on digests in the cache but older than this)
+ */
+ time_t horizon;
+ /*
+ * Digest map: keys are digests, values are times the digest was last seen
+ */
+ digest256map_t *digests_seen;
+};
+
+#endif /* defined(REPLAYCACHE_PRIVATE) */
+
+/* replaycache_t free/new */
+
+void replaycache_free_(replaycache_t *r);
+#define replaycache_free(r) \
+ FREE_AND_NULL(replaycache_t, replaycache_free_, (r))
+replaycache_t * replaycache_new(time_t horizon, time_t interval);
+
+#ifdef REPLAYCACHE_PRIVATE
+
+/*
+ * replaycache_t internal functions:
+ *
+ * These take the time to treat as the present as an argument for easy unit
+ * testing. For everything else, use the wrappers below instead.
+ */
+
+STATIC int replaycache_add_and_test_internal(
+ time_t present, replaycache_t *r, const void *data, size_t len,
+ time_t *elapsed);
+STATIC void replaycache_scrub_if_needed_internal(
+ time_t present, replaycache_t *r);
+
+#endif /* defined(REPLAYCACHE_PRIVATE) */
+
+/*
+ * replaycache_t methods
+ */
+
+int replaycache_add_and_test(replaycache_t *r, const void *data, size_t len);
+int replaycache_add_test_and_elapsed(
+ replaycache_t *r, const void *data, size_t len, time_t *elapsed);
+void replaycache_scrub_if_needed(replaycache_t *r);
+
+#endif /* !defined(TOR_REPLAYCACHE_H) */
diff --git a/src/feature/hs_common/shared_random_client.c b/src/feature/hs_common/shared_random_client.c
new file mode 100644
index 0000000000..42a5b42f60
--- /dev/null
+++ b/src/feature/hs_common/shared_random_client.c
@@ -0,0 +1,260 @@
+/* Copyright (c) 2018-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random_client.c
+ * \brief This file contains functions that are from the shared random
+ * subsystem but used by many part of tor. The full feature is built
+ * as part of the dirauth module.
+ **/
+
+#define SHARED_RANDOM_CLIENT_PRIVATE
+#include "or/shared_random_client.h"
+
+#include "or/config.h"
+#include "or/voting_schedule.h"
+#include "or/networkstatus.h"
+#include "lib/encoding/binascii.h"
+
+#include "or/networkstatus_st.h"
+
+/* Convert a given srv object to a string for the control port. This doesn't
+ * fail and the srv object MUST be valid. */
+static char *
+srv_to_control_string(const sr_srv_t *srv)
+{
+ char *srv_str;
+ char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+ tor_assert(srv);
+
+ sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
+ tor_asprintf(&srv_str, "%s", srv_hash_encoded);
+ return srv_str;
+}
+
+/* Return the voting interval of the tor vote subsystem. */
+int
+get_voting_interval(void)
+{
+ int interval;
+ networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL));
+
+ if (consensus) {
+ interval = (int)(consensus->fresh_until - consensus->valid_after);
+ } else {
+ /* Same for both a testing and real network. We voluntarily ignore the
+ * InitialVotingInterval since it complexifies things and it doesn't
+ * affect the SR protocol. */
+ interval = get_options()->V3AuthVotingInterval;
+ }
+ tor_assert(interval > 0);
+ return interval;
+}
+
+/* Given the time <b>now</b>, return the start time of the current round of
+ * the SR protocol. For example, if it's 23:47:08, the current round thus
+ * started at 23:47:00 for a voting interval of 10 seconds. */
+time_t
+get_start_time_of_current_round(void)
+{
+ const or_options_t *options = get_options();
+ int voting_interval = get_voting_interval();
+ /* First, get the start time of the next round */
+ time_t next_start = voting_schedule_get_next_valid_after_time();
+ /* Now roll back next_start by a voting interval to find the start time of
+ the current round. */
+ time_t curr_start = voting_schedule_get_start_of_next_interval(
+ next_start - voting_interval - 1,
+ voting_interval,
+ options->TestingV3AuthVotingStartOffset);
+ return curr_start;
+}
+
+/*
+ * Public API
+ */
+
+/* Encode the given shared random value and put it in dst. Destination
+ * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
+void
+sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
+{
+ int ret;
+ /* Extra byte for the NULL terminated char. */
+ char buf[SR_SRV_VALUE_BASE64_LEN + 1];
+
+ tor_assert(dst);
+ tor_assert(srv);
+ tor_assert(dst_len >= sizeof(buf));
+
+ ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
+ sizeof(srv->value), 0);
+ /* Always expect the full length without the NULL byte. */
+ tor_assert(ret == (sizeof(buf) - 1));
+ tor_assert(ret <= (int) dst_len);
+ strlcpy(dst, buf, dst_len);
+}
+
+/* Return the current SRV string representation for the control port. Return a
+ * newly allocated string on success containing the value else "" if not found
+ * or if we don't have a valid consensus yet. */
+char *
+sr_get_current_for_control(void)
+{
+ char *srv_str;
+ const networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c && c->sr_info.current_srv) {
+ srv_str = srv_to_control_string(c->sr_info.current_srv);
+ } else {
+ srv_str = tor_strdup("");
+ }
+ return srv_str;
+}
+
+/* Return the previous SRV string representation for the control port. Return
+ * a newly allocated string on success containing the value else "" if not
+ * found or if we don't have a valid consensus yet. */
+char *
+sr_get_previous_for_control(void)
+{
+ char *srv_str;
+ const networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c && c->sr_info.previous_srv) {
+ srv_str = srv_to_control_string(c->sr_info.previous_srv);
+ } else {
+ srv_str = tor_strdup("");
+ }
+ return srv_str;
+}
+
+/* Return current shared random value from the latest consensus. Caller can
+ * NOT keep a reference to the returned pointer. Return NULL if none. */
+const sr_srv_t *
+sr_get_current(const networkstatus_t *ns)
+{
+ const networkstatus_t *consensus;
+
+ /* Use provided ns else get a live one */
+ if (ns) {
+ consensus = ns;
+ } else {
+ consensus = networkstatus_get_live_consensus(approx_time());
+ }
+ /* Ideally we would never be asked for an SRV without a live consensus. Make
+ * sure this assumption is correct. */
+ tor_assert_nonfatal(consensus);
+
+ if (consensus) {
+ return consensus->sr_info.current_srv;
+ }
+ return NULL;
+}
+
+/* Return previous shared random value from the latest consensus. Caller can
+ * NOT keep a reference to the returned pointer. Return NULL if none. */
+const sr_srv_t *
+sr_get_previous(const networkstatus_t *ns)
+{
+ const networkstatus_t *consensus;
+
+ /* Use provided ns else get a live one */
+ if (ns) {
+ consensus = ns;
+ } else {
+ consensus = networkstatus_get_live_consensus(approx_time());
+ }
+ /* Ideally we would never be asked for an SRV without a live consensus. Make
+ * sure this assumption is correct. */
+ tor_assert_nonfatal(consensus);
+
+ if (consensus) {
+ return consensus->sr_info.previous_srv;
+ }
+ return NULL;
+}
+
+/* Parse a list of arguments from a SRV value either from a vote, consensus
+ * or from our disk state and return a newly allocated srv object. NULL is
+ * returned on error.
+ *
+ * The arguments' order:
+ * num_reveals, value
+ */
+sr_srv_t *
+sr_parse_srv(const smartlist_t *args)
+{
+ char *value;
+ int ok, ret;
+ uint64_t num_reveals;
+ sr_srv_t *srv = NULL;
+
+ tor_assert(args);
+
+ if (smartlist_len(args) < 2) {
+ goto end;
+ }
+
+ /* First argument is the number of reveal values */
+ num_reveals = tor_parse_uint64(smartlist_get(args, 0),
+ 10, 0, UINT64_MAX, &ok, NULL);
+ if (!ok) {
+ goto end;
+ }
+ /* Second and last argument is the shared random value it self. */
+ value = smartlist_get(args, 1);
+ if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
+ goto end;
+ }
+
+ srv = tor_malloc_zero(sizeof(*srv));
+ srv->num_reveals = num_reveals;
+ /* We subtract one byte from the srclen because the function ignores the
+ * '=' character in the given buffer. This is broken but it's a documented
+ * behavior of the implementation. */
+ ret = base64_decode((char *) srv->value, sizeof(srv->value), value,
+ SR_SRV_VALUE_BASE64_LEN - 1);
+ if (ret != sizeof(srv->value)) {
+ tor_free(srv);
+ srv = NULL;
+ goto end;
+ }
+ end:
+ return srv;
+}
+
+/** Return the start time of the current SR protocol run. For example, if the
+ * time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this
+ * function should return 23/06/2017 00:00:00. */
+time_t
+sr_state_get_start_time_of_current_protocol_run(time_t now)
+{
+ int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+ int voting_interval = get_voting_interval();
+ /* Find the time the current round started. */
+ time_t beginning_of_current_round = get_start_time_of_current_round();
+
+ /* Get current SR protocol round */
+ int current_round = (now / voting_interval) % total_rounds;
+
+ /* Get start time by subtracting the time elapsed from the beginning of the
+ protocol run */
+ time_t time_elapsed_since_start_of_run = current_round * voting_interval;
+ return beginning_of_current_round - time_elapsed_since_start_of_run;
+}
+
+/** Return the time (in seconds) it takes to complete a full SR protocol phase
+ * (e.g. the commit phase). */
+unsigned int
+sr_state_get_phase_duration(void)
+{
+ return SHARED_RANDOM_N_ROUNDS * get_voting_interval();
+}
+
+/** Return the time (in seconds) it takes to complete a full SR protocol run */
+unsigned int
+sr_state_get_protocol_run_duration(void)
+{
+ int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+ return total_protocol_rounds * get_voting_interval();
+}
+
diff --git a/src/feature/hs_common/shared_random_client.h b/src/feature/hs_common/shared_random_client.h
new file mode 100644
index 0000000000..079829496c
--- /dev/null
+++ b/src/feature/hs_common/shared_random_client.h
@@ -0,0 +1,47 @@
+/* Copyright (c) 2018-2018, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random_client.h
+ * \brief Header file for shared_random_client.c.
+ **/
+
+#ifndef TOR_SHARED_RANDOM_CLIENT_H
+#define TOR_SHARED_RANDOM_CLIENT_H
+
+/* Dirauth module. */
+#include "or/dirauth/shared_random.h"
+
+/* Helper functions. */
+void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv);
+int get_voting_interval(void);
+
+/* Control port functions. */
+char *sr_get_current_for_control(void);
+char *sr_get_previous_for_control(void);
+
+/* SRV functions. */
+const sr_srv_t *sr_get_current(const networkstatus_t *ns);
+const sr_srv_t *sr_get_previous(const networkstatus_t *ns);
+sr_srv_t *sr_parse_srv(const smartlist_t *args);
+
+/*
+ * Shared Random State API
+ */
+
+/* Each protocol phase has 12 rounds */
+#define SHARED_RANDOM_N_ROUNDS 12
+/* Number of phase we have in a protocol. */
+#define SHARED_RANDOM_N_PHASES 2
+
+time_t sr_state_get_start_time_of_current_protocol_run(time_t now);
+unsigned int sr_state_get_phase_duration(void);
+unsigned int sr_state_get_protocol_run_duration(void);
+time_t get_start_time_of_current_round(void);
+
+#ifdef TOR_UNIT_TESTS
+
+#endif /* TOR_UNIT_TESTS */
+
+#endif /* TOR_SHARED_RANDOM_CLIENT_H */
+