Prechádzať zdrojové kódy

prop224: Build hsdir index for node_t

This hsdir index value is used to give an index value to all node_t (relays)
that supports HSDir v3. An index value is then computed using the blinded key
to know where to fetch/upload the service descriptor from/to.

To avoid computing that index value everytime the client/service needs it, we
do that everytime we get a new consensus which then doesn't change until the
next one. The downside is that we need to sort them once we need to compute
the set of responsible HSDir.

Finally, the "hs_index" function is also added but not used. It will be used
in later commits to compute which node_t is a responsible HSDir for the
service we want to fetch/upload the descriptor.

Signed-off-by: David Goulet <dgoulet@torproject.org>
David Goulet 7 rokov pred
rodič
commit
267bc7bc3b
6 zmenil súbory, kde vykonal 255 pridanie a 0 odobranie
  1. 113 0
      src/or/hs_common.c
  2. 31 0
      src/or/hs_common.h
  3. 78 0
      src/or/nodelist.c
  4. 6 0
      src/or/or.h
  5. 24 0
      src/or/shared_random.c
  6. 3 0
      src/or/shared_random.h

+ 113 - 0
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

+ 31 - 0
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

+ 78 - 0
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);
 }
 

+ 6 - 0
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

+ 24 - 0
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

+ 3 - 0
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 */