2014-11-12 20:29:05 +01:00
|
|
|
/* Copyright (c) 2001-2004, Roger Dingledine.
|
|
|
|
* Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
|
|
|
|
* Copyright (c) 2007-2014, The Tor Project, Inc. */
|
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \file dircollate.c
|
|
|
|
*
|
|
|
|
* \brief Collation code for figuring out which identities to vote for in
|
|
|
|
* the directory voting process.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DIRCOLLATE_PRIVATE
|
|
|
|
#include "dircollate.h"
|
2014-11-13 16:03:55 +01:00
|
|
|
#include "dirvote.h"
|
2014-11-12 20:29:05 +01:00
|
|
|
|
|
|
|
static void dircollator_collate_by_rsa(dircollator_t *dc);
|
2014-11-13 16:03:55 +01:00
|
|
|
static void dircollator_collate_by_ed25519(dircollator_t *dc);
|
2014-11-12 20:29:05 +01:00
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Hashtable entry mapping a pair of digests (actually an ed25519 key and an
|
|
|
|
* RSA SHA1 digest) to an array of vote_routerstatus_t. */
|
2014-11-13 16:03:55 +01:00
|
|
|
typedef struct ddmap_entry_s {
|
|
|
|
HT_ENTRY(ddmap_entry_s) node;
|
|
|
|
uint8_t d[DIGEST_LEN + DIGEST256_LEN];
|
2016-02-22 16:39:42 +01:00
|
|
|
/* The i'th member of this array corresponds to the vote_routerstatus_t (if
|
|
|
|
* any) received for this digest pair from the n'th voter. */
|
2014-11-13 16:03:55 +01:00
|
|
|
vote_routerstatus_t *vrs_lst[FLEXIBLE_ARRAY_MEMBER];
|
|
|
|
} ddmap_entry_t;
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Release all storage held by e. */
|
2014-11-13 16:03:55 +01:00
|
|
|
static void
|
|
|
|
ddmap_entry_free(ddmap_entry_t *e)
|
|
|
|
{
|
|
|
|
tor_free(e);
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Return a new empty ddmap_entry, with <b>n_votes</b> elements in vrs_list. */
|
2014-11-13 16:03:55 +01:00
|
|
|
static ddmap_entry_t *
|
|
|
|
ddmap_entry_new(int n_votes)
|
|
|
|
{
|
|
|
|
return tor_malloc_zero(STRUCT_OFFSET(ddmap_entry_t, vrs_lst) +
|
|
|
|
sizeof(vote_routerstatus_t *) * n_votes);
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
ddmap_entry_hash(const ddmap_entry_t *ent)
|
|
|
|
{
|
|
|
|
return (unsigned) siphash24g(ent->d, sizeof(ent->d));
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned
|
|
|
|
ddmap_entry_eq(const ddmap_entry_t *a, const ddmap_entry_t *b)
|
|
|
|
{
|
|
|
|
return fast_memeq(a->d, b->d, sizeof(a->d));
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Record the RSA identity of <b>ent</b> as <b>rsa_sha1</b>, and the
|
|
|
|
* ed25519 identity as <b>ed25519</b>. */
|
2014-11-13 16:03:55 +01:00
|
|
|
static void
|
|
|
|
ddmap_entry_set_digests(ddmap_entry_t *ent,
|
|
|
|
const uint8_t *rsa_sha1,
|
|
|
|
const uint8_t *ed25519)
|
|
|
|
{
|
|
|
|
memcpy(ent->d, rsa_sha1, DIGEST_LEN);
|
|
|
|
memcpy(ent->d + DIGEST_LEN, ed25519, DIGEST256_LEN);
|
|
|
|
}
|
|
|
|
|
2015-06-01 14:59:14 +02:00
|
|
|
HT_PROTOTYPE(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash,
|
|
|
|
ddmap_entry_eq);
|
|
|
|
HT_GENERATE2(double_digest_map, ddmap_entry_s, node, ddmap_entry_hash,
|
|
|
|
ddmap_entry_eq, 0.6, tor_reallocarray, tor_free_);
|
2016-02-22 16:39:42 +01:00
|
|
|
|
|
|
|
/** Helper: add a single vote_routerstatus_t <b>vrs</b> to the collator
|
|
|
|
* <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of
|
|
|
|
* its RSA key digest and Ed25519 key. */
|
2014-11-12 20:29:05 +01:00
|
|
|
static void
|
|
|
|
dircollator_add_routerstatus(dircollator_t *dc,
|
|
|
|
int vote_num,
|
|
|
|
networkstatus_t *vote,
|
|
|
|
vote_routerstatus_t *vrs)
|
|
|
|
{
|
|
|
|
const char *id = vrs->status.identity_digest;
|
|
|
|
|
Fix another case of 17668: Add NoEdConsensus
I had a half-built mechanism to track, during the voting process,
whether the Ed25519 value (or lack thereof) reflected a true
consensus among the authorities. But we never actually inserted this
field in the consensus.
The key idea here is that we first attempt to match up votes by pairs
of <Ed,RSA>, where <Ed> can be NULL if we're told that there is no
Ed key. If this succeeds, then we can treat all those votes as 'a
consensus for Ed'. And we can include all other votes with a
matching RSA key and no statement about Ed keys as being "also about
the same relay."
After that, we look for RSA keys we haven't actually found an entry
for yet, and see if there are enough votes for them, NOT considering
Ed keys. If there are, we match them as before, but we treat them
as "not a consensus about ed".
When we include an entry in a consensus, if it does not reflect a
consensus about ed keys, then we include a new NoEdConsensus flag on
it.
This is all only for consensus method 22 or later.
Also see corresponding dir-spec patch.
2016-02-23 15:31:23 +01:00
|
|
|
vrs->ed25519_reflects_consensus = 0;
|
|
|
|
|
2014-11-12 20:29:05 +01:00
|
|
|
(void) vote;
|
|
|
|
vote_routerstatus_t **vrs_lst = digestmap_get(dc->by_rsa_sha1, id);
|
|
|
|
if (NULL == vrs_lst) {
|
|
|
|
vrs_lst = tor_calloc(sizeof(vote_routerstatus_t *), dc->n_votes);
|
|
|
|
digestmap_set(dc->by_rsa_sha1, id, vrs_lst);
|
|
|
|
}
|
2014-11-13 16:03:55 +01:00
|
|
|
tor_assert(vrs_lst[vote_num] == NULL);
|
|
|
|
vrs_lst[vote_num] = vrs;
|
2014-11-12 20:29:05 +01:00
|
|
|
|
2014-11-13 16:03:55 +01:00
|
|
|
const uint8_t *ed = vrs->ed25519_id;
|
|
|
|
|
Fix another case of 17668: Add NoEdConsensus
I had a half-built mechanism to track, during the voting process,
whether the Ed25519 value (or lack thereof) reflected a true
consensus among the authorities. But we never actually inserted this
field in the consensus.
The key idea here is that we first attempt to match up votes by pairs
of <Ed,RSA>, where <Ed> can be NULL if we're told that there is no
Ed key. If this succeeds, then we can treat all those votes as 'a
consensus for Ed'. And we can include all other votes with a
matching RSA key and no statement about Ed keys as being "also about
the same relay."
After that, we look for RSA keys we haven't actually found an entry
for yet, and see if there are enough votes for them, NOT considering
Ed keys. If there are, we match them as before, but we treat them
as "not a consensus about ed".
When we include an entry in a consensus, if it does not reflect a
consensus about ed keys, then we include a new NoEdConsensus flag on
it.
This is all only for consensus method 22 or later.
Also see corresponding dir-spec patch.
2016-02-23 15:31:23 +01:00
|
|
|
if (! vrs->has_ed25519_listing)
|
2014-11-13 16:03:55 +01:00
|
|
|
return;
|
|
|
|
|
|
|
|
ddmap_entry_t search, *found;
|
|
|
|
memset(&search, 0, sizeof(search));
|
|
|
|
ddmap_entry_set_digests(&search, (const uint8_t *)id, ed);
|
|
|
|
found = HT_FIND(double_digest_map, &dc->by_both_ids, &search);
|
|
|
|
if (NULL == found) {
|
|
|
|
found = ddmap_entry_new(dc->n_votes);
|
|
|
|
ddmap_entry_set_digests(found, (const uint8_t *)id, ed);
|
|
|
|
HT_INSERT(double_digest_map, &dc->by_both_ids, found);
|
|
|
|
}
|
|
|
|
vrs_lst = found->vrs_lst;
|
2014-11-12 20:29:05 +01:00
|
|
|
tor_assert(vrs_lst[vote_num] == NULL);
|
|
|
|
vrs_lst[vote_num] = vrs;
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Create and return a new dircollator object to use when collating
|
|
|
|
* <b>n_votes</b> out of a total of <b>n_authorities</b>. */
|
2014-11-12 20:29:05 +01:00
|
|
|
dircollator_t *
|
|
|
|
dircollator_new(int n_votes, int n_authorities)
|
|
|
|
{
|
|
|
|
dircollator_t *dc = tor_malloc_zero(sizeof(dircollator_t));
|
|
|
|
|
|
|
|
tor_assert(n_votes <= n_authorities);
|
|
|
|
|
|
|
|
dc->n_votes = n_votes;
|
|
|
|
dc->n_authorities = n_authorities;
|
|
|
|
|
|
|
|
dc->by_rsa_sha1 = digestmap_new();
|
2014-11-13 16:03:55 +01:00
|
|
|
HT_INIT(double_digest_map, &dc->by_both_ids);
|
2014-11-12 20:29:05 +01:00
|
|
|
|
|
|
|
return dc;
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Release all storage held by <b>dc</b>. */
|
2014-11-12 20:29:05 +01:00
|
|
|
void
|
|
|
|
dircollator_free(dircollator_t *dc)
|
|
|
|
{
|
|
|
|
if (!dc)
|
|
|
|
return;
|
|
|
|
|
2014-11-13 16:03:55 +01:00
|
|
|
if (dc->by_collated_rsa_sha1 != dc->by_rsa_sha1)
|
|
|
|
digestmap_free(dc->by_collated_rsa_sha1, NULL);
|
|
|
|
|
2014-11-12 20:29:05 +01:00
|
|
|
digestmap_free(dc->by_rsa_sha1, tor_free_);
|
2015-06-01 16:26:11 +02:00
|
|
|
smartlist_free(dc->all_rsa_sha1_lst);
|
2014-11-12 20:29:05 +01:00
|
|
|
|
2014-11-13 16:03:55 +01:00
|
|
|
ddmap_entry_t **e, **next, *this;
|
|
|
|
for (e = HT_START(double_digest_map, &dc->by_both_ids);
|
|
|
|
e != NULL; e = next) {
|
|
|
|
this = *e;
|
|
|
|
next = HT_NEXT_RMV(double_digest_map, &dc->by_both_ids, e);
|
|
|
|
ddmap_entry_free(this);
|
|
|
|
}
|
|
|
|
HT_CLEAR(double_digest_map, &dc->by_both_ids);
|
|
|
|
|
2014-11-12 20:29:05 +01:00
|
|
|
tor_free(dc);
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Add a single vote <b>v</b> to a dircollator <b>dc</b>. This function must
|
|
|
|
* be called exactly once for each vote to be used in the consensus. It may
|
|
|
|
* only be called before dircollator_collate().
|
|
|
|
*/
|
2014-11-12 20:29:05 +01:00
|
|
|
void
|
|
|
|
dircollator_add_vote(dircollator_t *dc, networkstatus_t *v)
|
|
|
|
{
|
|
|
|
tor_assert(v->type == NS_TYPE_VOTE);
|
|
|
|
tor_assert(dc->next_vote_num < dc->n_votes);
|
|
|
|
tor_assert(!dc->is_collated);
|
|
|
|
|
|
|
|
const int votenum = dc->next_vote_num++;
|
|
|
|
|
|
|
|
SMARTLIST_FOREACH_BEGIN(v->routerstatus_list, vote_routerstatus_t *, vrs) {
|
|
|
|
dircollator_add_routerstatus(dc, votenum, v, vrs);
|
|
|
|
} SMARTLIST_FOREACH_END(vrs);
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Sort the entries in <b>dc</b> according to <b>consensus_method</b>, so
|
|
|
|
* that the consensus process can iterate over them with
|
|
|
|
* dircollator_n_routers() and dircollator_get_votes_for_router(). */
|
2014-11-12 20:29:05 +01:00
|
|
|
void
|
2014-11-13 16:03:55 +01:00
|
|
|
dircollator_collate(dircollator_t *dc, int consensus_method)
|
2014-11-12 20:29:05 +01:00
|
|
|
{
|
2014-11-13 16:03:55 +01:00
|
|
|
tor_assert(!dc->is_collated);
|
|
|
|
dc->all_rsa_sha1_lst = smartlist_new();
|
|
|
|
|
2016-02-22 16:07:42 +01:00
|
|
|
if (consensus_method < MIN_METHOD_FOR_ED25519_ID_VOTING)
|
2014-11-13 16:03:55 +01:00
|
|
|
dircollator_collate_by_rsa(dc);
|
|
|
|
else
|
|
|
|
dircollator_collate_by_ed25519(dc);
|
|
|
|
|
|
|
|
smartlist_sort_digests(dc->all_rsa_sha1_lst);
|
|
|
|
dc->is_collated = 1;
|
2014-11-12 20:29:05 +01:00
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/**
|
|
|
|
* Collation function for RSA-only consensuses: collate the votes for each
|
|
|
|
* entry in <b>dc</b> by their RSA keys.
|
|
|
|
*
|
|
|
|
* The rule is:
|
|
|
|
* If an RSA identity key is listed by more than half of the authorities,
|
|
|
|
* include that identity, and treat all descriptors with that RSA identity
|
|
|
|
* as describing the same router.
|
|
|
|
*/
|
2014-11-12 20:29:05 +01:00
|
|
|
static void
|
|
|
|
dircollator_collate_by_rsa(dircollator_t *dc)
|
|
|
|
{
|
2014-11-13 16:03:55 +01:00
|
|
|
const int total_authorities = dc->n_authorities;
|
2014-11-12 20:29:05 +01:00
|
|
|
|
2014-11-13 16:03:55 +01:00
|
|
|
DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) {
|
|
|
|
int n = 0, i;
|
|
|
|
for (i = 0; i < dc->n_votes; ++i) {
|
|
|
|
if (vrs_lst[i] != NULL)
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n <= total_authorities / 2)
|
|
|
|
continue;
|
2014-11-12 20:29:05 +01:00
|
|
|
|
2014-11-13 16:03:55 +01:00
|
|
|
smartlist_add(dc->all_rsa_sha1_lst, (char *)k);
|
|
|
|
} DIGESTMAP_FOREACH_END;
|
|
|
|
|
|
|
|
dc->by_collated_rsa_sha1 = dc->by_rsa_sha1;
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/**
|
|
|
|
* Collation function for ed25519 consensuses: collate the votes for each
|
|
|
|
* entry in <b>dc</b> by ed25519 key and by RSA key.
|
|
|
|
*
|
|
|
|
* The rule is, approximately:
|
|
|
|
* If a <ed,rsa> identity is listed by more than half of authorities,
|
|
|
|
* include it. And include all <rsa>-only votes about that node as
|
|
|
|
* matching.
|
|
|
|
*
|
|
|
|
* Otherwise, if an <*,rsa> or <rsa> identity is listed by more than
|
|
|
|
* half of the authorities, and no <ed,rsa> pair for the same RSA key
|
|
|
|
* has been already been included based on the rule above, include
|
|
|
|
* that RSA identity.
|
|
|
|
*/
|
2014-11-13 16:03:55 +01:00
|
|
|
static void
|
|
|
|
dircollator_collate_by_ed25519(dircollator_t *dc)
|
|
|
|
{
|
2014-11-12 20:29:05 +01:00
|
|
|
const int total_authorities = dc->n_authorities;
|
2014-11-13 16:03:55 +01:00
|
|
|
digestmap_t *rsa_digests = digestmap_new();
|
|
|
|
|
|
|
|
ddmap_entry_t **iter;
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/* Go over all <ed,rsa> pairs */
|
2014-11-13 16:03:55 +01:00
|
|
|
HT_FOREACH(iter, double_digest_map, &dc->by_both_ids) {
|
|
|
|
ddmap_entry_t *ent = *iter;
|
|
|
|
int n = 0, i;
|
|
|
|
for (i = 0; i < dc->n_votes; ++i) {
|
|
|
|
if (ent->vrs_lst[i] != NULL)
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/* If not enough authorties listed this exact <ed,rsa> pair,
|
|
|
|
* don't include it. */
|
2014-11-13 16:03:55 +01:00
|
|
|
if (n <= total_authorities / 2)
|
|
|
|
continue;
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/* Now consider whether there are any other entries with the same
|
|
|
|
* RSA key (but with possibly different or missing ed value). */
|
2014-11-13 16:03:55 +01:00
|
|
|
vote_routerstatus_t **vrs_lst2 = digestmap_get(dc->by_rsa_sha1,
|
|
|
|
(char*)ent->d);
|
|
|
|
tor_assert(vrs_lst2);
|
|
|
|
|
|
|
|
for (i = 0; i < dc->n_votes; ++i) {
|
|
|
|
if (ent->vrs_lst[i] != NULL) {
|
|
|
|
ent->vrs_lst[i]->ed25519_reflects_consensus = 1;
|
|
|
|
} else if (vrs_lst2[i] && ! vrs_lst2[i]->has_ed25519_listing) {
|
|
|
|
ent->vrs_lst[i] = vrs_lst2[i];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/* Record that we have seen this RSA digest. */
|
2014-11-13 16:03:55 +01:00
|
|
|
digestmap_set(rsa_digests, (char*)ent->d, ent->vrs_lst);
|
|
|
|
smartlist_add(dc->all_rsa_sha1_lst, ent->d);
|
|
|
|
}
|
2014-11-12 20:29:05 +01:00
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/* Now look over all entries with an RSA digest, looking for RSA digests
|
|
|
|
* we didn't put in yet.
|
|
|
|
*/
|
2014-11-12 20:29:05 +01:00
|
|
|
DIGESTMAP_FOREACH(dc->by_rsa_sha1, k, vote_routerstatus_t **, vrs_lst) {
|
2014-11-13 16:03:55 +01:00
|
|
|
if (digestmap_get(rsa_digests, k) != NULL)
|
2016-02-22 16:39:42 +01:00
|
|
|
continue; /* We already included this RSA digest */
|
2014-11-13 16:03:55 +01:00
|
|
|
|
2014-11-12 20:29:05 +01:00
|
|
|
int n = 0, i;
|
|
|
|
for (i = 0; i < dc->n_votes; ++i) {
|
|
|
|
if (vrs_lst[i] != NULL)
|
|
|
|
++n;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (n <= total_authorities / 2)
|
2016-02-22 16:39:42 +01:00
|
|
|
continue; /* Not enough votes */
|
2014-11-12 20:29:05 +01:00
|
|
|
|
2014-11-13 16:03:55 +01:00
|
|
|
digestmap_set(rsa_digests, k, vrs_lst);
|
2014-11-12 20:29:05 +01:00
|
|
|
smartlist_add(dc->all_rsa_sha1_lst, (char *)k);
|
|
|
|
} DIGESTMAP_FOREACH_END;
|
|
|
|
|
2014-11-13 16:03:55 +01:00
|
|
|
dc->by_collated_rsa_sha1 = rsa_digests;
|
2014-11-12 20:29:05 +01:00
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Return the total number of collated router entries. This function may
|
|
|
|
* only be called after dircollator_collate. */
|
2014-11-12 20:29:05 +01:00
|
|
|
int
|
|
|
|
dircollator_n_routers(dircollator_t *dc)
|
|
|
|
{
|
|
|
|
return smartlist_len(dc->all_rsa_sha1_lst);
|
|
|
|
}
|
|
|
|
|
2016-02-22 16:39:42 +01:00
|
|
|
/** Return an array of vote_routerstatus_t entries for the <b>idx</b>th router
|
|
|
|
* in the collation order. Each array contains n_votes elements, where the
|
|
|
|
* nth element of the array is the vote_routerstatus_t from the nth voter for
|
|
|
|
* this identity (or NULL if there is no such entry).
|
|
|
|
*
|
|
|
|
* The maximum value for <b>idx</b> is dircollator_n_routers().
|
|
|
|
*
|
|
|
|
* This function may only be called after dircollator_collate. */
|
2014-11-12 20:29:05 +01:00
|
|
|
vote_routerstatus_t **
|
|
|
|
dircollator_get_votes_for_router(dircollator_t *dc, int idx)
|
|
|
|
{
|
|
|
|
tor_assert(idx < smartlist_len(dc->all_rsa_sha1_lst));
|
2014-11-13 16:03:55 +01:00
|
|
|
return digestmap_get(dc->by_collated_rsa_sha1,
|
2014-11-12 20:29:05 +01:00
|
|
|
smartlist_get(dc->all_rsa_sha1_lst, idx));
|
|
|
|
}
|
|
|
|
|