summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/or/hs_common.c113
-rw-r--r--src/or/hs_common.h31
-rw-r--r--src/or/nodelist.c78
-rw-r--r--src/or/or.h6
-rw-r--r--src/or/shared_random.c24
-rw-r--r--src/or/shared_random.h3
6 files changed, 255 insertions, 0 deletions
diff --git a/src/or/hs_common.c b/src/or/hs_common.c
index 571f4c5178..0e3562de87 100644
--- a/src/or/hs_common.c
+++ b/src/or/hs_common.c
@@ -20,6 +20,7 @@
#include "hs_service.h"
#include "rendcommon.h"
#include "rendservice.h"
+#include "shared_random.h"
/* Ed25519 Basepoint value. Taken from section 5 of
* https://tools.ietf.org/html/draft-josefsson-eddsa-ed25519-03 */
@@ -369,6 +370,25 @@ rend_data_get_pk_digest(const rend_data_t *rend_data, size_t *len_out)
}
}
+/* Using the given time period number, compute the disaster shared random
+ * value and put it in srv_out. It MUST be at least DIGEST256_LEN bytes. */
+static void
+get_disaster_srv(uint64_t time_period_num, uint8_t *srv_out)
+{
+ crypto_digest_t *digest;
+
+ tor_assert(srv_out);
+
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+ /* Setup payload: H("shared-random-disaster" | INT_8(period_num)) */
+ crypto_digest_add_bytes(digest, HS_SRV_DISASTER_PREFIX,
+ HS_SRV_DISASTER_PREFIX_LEN);
+ crypto_digest_add_bytes(digest, (const char *) &time_period_num,
+ sizeof(time_period_num));
+ crypto_digest_get_digest(digest, (char *) srv_out, DIGEST256_LEN);
+ crypto_digest_free(digest);
+}
+
/* When creating a blinded key, we need a parameter which construction is as
* follow: H(pubkey | [secret] | ed25519-basepoint | nonce).
*
@@ -744,6 +764,99 @@ hs_service_requires_uptime_circ(const smartlist_t *ports)
return 0;
}
+/* Build hs_index which is used to find the responsible hsdirs. This index
+ * value is used to select the responsible HSDir where their hsdir_index is
+ * closest to this value.
+ * SHA3-256("store-at-idx" | blinded_public_key |
+ * INT_8(replicanum) | INT_8(period_num) )
+ *
+ * hs_index_out must be large enough to receive DIGEST256_LEN bytes. */
+void
+hs_build_hs_index(uint64_t replica, const ed25519_public_key_t *blinded_pk,
+ uint64_t period_num, uint8_t *hs_index_out)
+{
+ crypto_digest_t *digest;
+
+ tor_assert(blinded_pk);
+ tor_assert(hs_index_out);
+
+ /* Build hs_index. See construction at top of function comment. */
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+ crypto_digest_add_bytes(digest, HS_INDEX_PREFIX, HS_INDEX_PREFIX_LEN);
+ crypto_digest_add_bytes(digest, (const char *) blinded_pk->pubkey,
+ ED25519_PUBKEY_LEN);
+ crypto_digest_add_bytes(digest, (const char *) &replica, sizeof(replica));
+ crypto_digest_add_bytes(digest, (const char *) &period_num,
+ sizeof(period_num));
+ crypto_digest_get_digest(digest, (char *) hs_index_out, DIGEST256_LEN);
+ crypto_digest_free(digest);
+}
+
+/* Build hsdir_index which is used to find the responsible hsdirs. This is the
+ * index value that is compare to the hs_index when selecting an HSDir.
+ * SHA3-256("node-idx" | node_identity |
+ * shared_random_value | INT_8(period_num) )
+ *
+ * hsdir_index_out must be large enough to receive DIGEST256_LEN bytes. */
+void
+hs_build_hsdir_index(const ed25519_public_key_t *identity_pk,
+ const uint8_t *srv_value, uint64_t period_num,
+ uint8_t *hsdir_index_out)
+{
+ crypto_digest_t *digest;
+
+ tor_assert(identity_pk);
+ tor_assert(srv_value);
+ tor_assert(hsdir_index_out);
+
+ /* Build hsdir_index. See construction at top of function comment. */
+ digest = crypto_digest256_new(DIGEST_SHA3_256);
+ crypto_digest_add_bytes(digest, HSDIR_INDEX_PREFIX, HSDIR_INDEX_PREFIX_LEN);
+ crypto_digest_add_bytes(digest, (const char *) identity_pk->pubkey,
+ ED25519_PUBKEY_LEN);
+ crypto_digest_add_bytes(digest, (const char *) srv_value, DIGEST256_LEN);
+ crypto_digest_add_bytes(digest, (const char *) &period_num,
+ sizeof(period_num));
+ crypto_digest_get_digest(digest, (char *) hsdir_index_out, DIGEST256_LEN);
+ crypto_digest_free(digest);
+}
+
+/* Return a newly allocated buffer containing the current shared random value
+ * or if not present, a disaster value is computed using the given time period
+ * number. This function can't fail. */
+uint8_t *
+hs_get_current_srv(uint64_t time_period_num)
+{
+ uint8_t *sr_value = tor_malloc_zero(DIGEST256_LEN);
+ const sr_srv_t *current_srv = sr_get_current();
+
+ if (current_srv) {
+ memcpy(sr_value, current_srv->value, sizeof(current_srv->value));
+ } else {
+ /* Disaster mode. */
+ get_disaster_srv(time_period_num, sr_value);
+ }
+ return sr_value;
+}
+
+/* Return a newly allocated buffer containing the previous shared random
+ * value or if not present, a disaster value is computed using the given time
+ * period number. This function can't fail. */
+uint8_t *
+hs_get_previous_srv(uint64_t time_period_num)
+{
+ uint8_t *sr_value = tor_malloc_zero(DIGEST256_LEN);
+ const sr_srv_t *previous_srv = sr_get_previous();
+
+ if (previous_srv) {
+ memcpy(sr_value, previous_srv->value, sizeof(previous_srv->value));
+ } else {
+ /* Disaster mode. */
+ get_disaster_srv(time_period_num, sr_value);
+ }
+ return sr_value;
+}
+
/* Initialize the entire HS subsytem. This is called in tor_init() before any
* torrc options are loaded. Only for >= v3. */
void
diff --git a/src/or/hs_common.h b/src/or/hs_common.h
index f9e3f297a9..a70ddc68da 100644
--- a/src/or/hs_common.h
+++ b/src/or/hs_common.h
@@ -101,6 +101,18 @@
#define HS_SUBCREDENTIAL_PREFIX "subcredential"
#define HS_SUBCREDENTIAL_PREFIX_LEN (sizeof(HS_SUBCREDENTIAL_PREFIX) - 1)
+/* Node hidden service stored at index prefix value. */
+#define HS_INDEX_PREFIX "store-at-idx"
+#define HS_INDEX_PREFIX_LEN (sizeof(HS_INDEX_PREFIX) - 1)
+
+/* Node hidden service directory index prefix value. */
+#define HSDIR_INDEX_PREFIX "node-idx"
+#define HSDIR_INDEX_PREFIX_LEN (sizeof(HSDIR_INDEX_PREFIX) - 1)
+
+/* Prefix of the shared random value disaster mode. */
+#define HS_SRV_DISASTER_PREFIX "shared-random-disaster"
+#define HS_SRV_DISASTER_PREFIX_LEN (sizeof(HS_SRV_DISASTER_PREFIX) - 1)
+
/* Type of authentication key used by an introduction point. */
typedef enum {
HS_AUTH_KEY_TYPE_LEGACY = 1,
@@ -122,6 +134,15 @@ typedef struct rend_service_port_config_t {
char unix_addr[FLEXIBLE_ARRAY_MEMBER];
} rend_service_port_config_t;
+/* Hidden service directory index used in a node_t which is set once we set
+ * the consensus. */
+typedef struct hsdir_index_t {
+ /* The hsdir index for the current time period. */
+ uint8_t current[DIGEST256_LEN];
+ /* The hsdir index for the next time period. */
+ uint8_t next[DIGEST256_LEN];
+} hsdir_index_t;
+
void hs_init(void);
void hs_free_all(void);
@@ -172,6 +193,16 @@ link_specifier_t *hs_link_specifier_dup(const link_specifier_t *lspec);
int hs_overlap_mode_is_active(const networkstatus_t *consensus, time_t now);
+uint8_t *hs_get_current_srv(uint64_t time_period_num);
+uint8_t *hs_get_previous_srv(uint64_t time_period_num);
+
+void hs_build_hsdir_index(const ed25519_public_key_t *identity_pk,
+ const uint8_t *srv, uint64_t period_num,
+ uint8_t *hsdir_index_out);
+void hs_build_hs_index(uint64_t replica,
+ const ed25519_public_key_t *blinded_pk,
+ uint64_t period_num, uint8_t *hs_index_out);
+
#ifdef HS_COMMON_PRIVATE
#ifdef TOR_UNIT_TESTS
diff --git a/src/or/nodelist.c b/src/or/nodelist.c
index dafeb9f12d..117598cf1b 100644
--- a/src/or/nodelist.c
+++ b/src/or/nodelist.c
@@ -45,6 +45,7 @@
#include "dirserv.h"
#include "entrynodes.h"
#include "geoip.h"
+#include "hs_common.h"
#include "main.h"
#include "microdesc.h"
#include "networkstatus.h"
@@ -164,12 +165,71 @@ node_get_or_create(const char *identity_digest)
smartlist_add(the_nodelist->nodes, node);
node->nodelist_idx = smartlist_len(the_nodelist->nodes) - 1;
+ node->hsdir_index = tor_malloc_zero(sizeof(hsdir_index_t));
node->country = -1;
return node;
}
+/* For a given <b>node</b> for the consensus <b>ns</b>, set the hsdir index
+ * for the node, both current and next if possible. This can only fails if the
+ * node_t ed25519 identity key can't be found which would be a bug. */
+static void
+node_set_hsdir_index(node_t *node, const networkstatus_t *ns)
+{
+ time_t now = time(NULL);
+ const ed25519_public_key_t *node_identity_pk;
+ uint8_t *next_hsdir_index_srv = NULL, *current_hsdir_index_srv = NULL;
+ uint64_t next_time_period_num, current_time_period_num;
+
+ tor_assert(node);
+ tor_assert(ns);
+
+ node_identity_pk = node_get_ed25519_id(node);
+ if (node_identity_pk == NULL) {
+ log_warn(LD_BUG, "ed25519 identity public key not found when "
+ "trying to build the hsdir indexes for node %s",
+ node_describe(node));
+ goto done;
+ }
+
+ /* Get the current and next time period number, we might use them both. */
+ current_time_period_num = hs_get_time_period_num(now);
+ next_time_period_num = hs_get_next_time_period_num(now);
+
+ /* If NOT in overlap mode, we only need to compute the current hsdir index
+ * for the ongoing time period and thus the current SRV. If it can't be
+ * found, the disaster one is returned. */
+ current_hsdir_index_srv = hs_get_current_srv(current_time_period_num);
+
+ if (hs_overlap_mode_is_active(ns, now)) {
+ /* We are in overlap mode, this means that our consensus has just cycled
+ * from current SRV to previous SRV so for the _next_ upcoming time
+ * period, we have to use the current SRV and use the previous SRV for the
+ * current time period. If the current or previous SRV can't be found, the
+ * disaster one is returned. */
+ next_hsdir_index_srv = hs_get_current_srv(next_time_period_num);
+ /* The following can be confusing so again, in overlap mode, we use our
+ * previous SRV for our _current_ hsdir index. */
+ current_hsdir_index_srv = hs_get_previous_srv(current_time_period_num);
+ }
+
+ /* Build the current hsdir index. */
+ hs_build_hsdir_index(node_identity_pk, current_hsdir_index_srv,
+ current_time_period_num, node->hsdir_index->current);
+ if (next_hsdir_index_srv) {
+ /* Build the next hsdir index if we have a next SRV that we can use. */
+ hs_build_hsdir_index(node_identity_pk, next_hsdir_index_srv,
+ next_time_period_num, node->hsdir_index->next);
+ }
+
+ done:
+ tor_free(current_hsdir_index_srv);
+ tor_free(next_hsdir_index_srv);
+ return;
+}
+
/** Called when a node's address changes. */
static void
node_addrs_changed(node_t *node)
@@ -216,6 +276,14 @@ nodelist_set_routerinfo(routerinfo_t *ri, routerinfo_t **ri_old_out)
dirserv_set_node_flags_from_authoritative_status(node, status);
}
+ /* Setting the HSDir index requires the ed25519 identity key which can
+ * only be found either in the ri or md. This is why this is called here.
+ * Only nodes supporting HSDir=2 protocol version needs this index. */
+ if (node->rs && node->rs->supports_v3_hsdir) {
+ node_set_hsdir_index(node,
+ networkstatus_get_latest_consensus());
+ }
+
return node;
}
@@ -246,6 +314,12 @@ nodelist_add_microdesc(microdesc_t *md)
node->md->held_by_nodes--;
node->md = md;
md->held_by_nodes++;
+ /* Setting the HSDir index requires the ed25519 identity key which can
+ * only be found either in the ri or md. This is why this is called here.
+ * Only nodes supporting HSDir=2 protocol version needs this index. */
+ if (rs->supports_v3_hsdir) {
+ node_set_hsdir_index(node, ns);
+ }
}
return node;
}
@@ -283,6 +357,9 @@ nodelist_set_consensus(networkstatus_t *ns)
}
}
+ if (rs->supports_v3_hsdir) {
+ node_set_hsdir_index(node, ns);
+ }
node_set_country(node);
/* If we're not an authdir, believe others. */
@@ -410,6 +487,7 @@ node_free(node_t *node)
if (node->md)
node->md->held_by_nodes--;
tor_assert(node->nodelist_idx == -1);
+ tor_free(node->hsdir_index);
tor_free(node);
}
diff --git a/src/or/or.h b/src/or/or.h
index f6c42b7a99..a06c816e8b 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -850,6 +850,8 @@ rend_data_v2_t *TO_REND_DATA_V2(const rend_data_t *d)
struct hs_ident_edge_conn_t;
struct hs_ident_dir_conn_t;
struct hs_ident_circuit_t;
+/* Stub because we can't include hs_common.h. */
+struct hsdir_index_t;
/** Time interval for tracking replays of DH public keys received in
* INTRODUCE2 cells. Used only to avoid launching multiple
@@ -2490,6 +2492,10 @@ typedef struct node_t {
time_t last_reachable; /* IPv4. */
time_t last_reachable6; /* IPv6. */
+ /* Hidden service directory index data. This is used by a service or client
+ * in order to know what's the hs directory index for this node at the time
+ * the consensus is set. */
+ struct hsdir_index_t *hsdir_index;
} node_t;
/** Linked list of microdesc hash lines for a single router in a directory
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 25ca0611cd..ec2533dad2 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -1390,6 +1390,30 @@ sr_get_previous_for_control(void)
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(void)
+{
+ const networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c) {
+ return c->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(void)
+{
+ const networkstatus_t *c = networkstatus_get_latest_consensus();
+ if (c) {
+ return c->sr_info.previous_srv;
+ }
+ return NULL;
+}
+
#ifdef TOR_UNIT_TESTS
/* Set the global value of number of SRV agreements so the test can play
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index 1f027c70e0..58ea360df0 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -130,6 +130,9 @@ sr_commit_t *sr_generate_our_commit(time_t timestamp,
char *sr_get_current_for_control(void);
char *sr_get_previous_for_control(void);
+const sr_srv_t *sr_get_current(void);
+const sr_srv_t *sr_get_previous(void);
+
#ifdef SHARED_RANDOM_PRIVATE
/* Encode */