diff options
-rw-r--r-- | src/or/hs_common.c | 113 | ||||
-rw-r--r-- | src/or/hs_common.h | 31 | ||||
-rw-r--r-- | src/or/nodelist.c | 78 | ||||
-rw-r--r-- | src/or/or.h | 6 | ||||
-rw-r--r-- | src/or/shared_random.c | 24 | ||||
-rw-r--r-- | src/or/shared_random.h | 3 |
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 */ |