1 /* Copyright (c) 2001-2004, Roger Dingledine.
2 * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
3 * Copyright (c) 2007-2021, The Tor Project, Inc. */
4 /* See LICENSE for licensing information */
9 * \brief Collation code for figuring out which identities to vote for in
10 * the directory voting process.
12 * During the consensus calculation, when an authority is looking at the vote
13 * documents from all the authorities, it needs to compute the consensus for
14 * each relay listed by at least one authority. But the notion of "each
15 * relay" can be tricky: some relays have Ed25519 keys, and others don't.
17 * Moreover, older consensus methods did RSA-based ID collation alone, and
18 * ignored Ed25519 keys. We need to support those too until we're completely
19 * sure that authorities will never downgrade.
21 * This module is invoked exclusively from dirvote.c.
24 #define DIRCOLLATE_PRIVATE
25 #include "feature/dirauth/dircollate.h"
26 #include "feature/dirauth/dirvote.h"
28 #include "feature/nodelist/networkstatus_st.h"
29 #include "feature/nodelist/vote_routerstatus_st.h"
31 static void dircollator_collate_by_ed25519(dircollator_t
*dc
);
33 /** Hashtable entry mapping a pair of digests (actually an ed25519 key and an
34 * RSA SHA1 digest) to an array of vote_routerstatus_t. */
35 typedef struct ddmap_entry_t
{
36 HT_ENTRY(ddmap_entry_t
) node
;
37 /** A SHA1-RSA1024 identity digest and Ed25519 identity key,
38 * concatenated. (If there is no ed25519 identity key, there is no
39 * entry in this table.) */
40 uint8_t d
[DIGEST_LEN
+ DIGEST256_LEN
];
41 /* The nth member of this array corresponds to the vote_routerstatus_t (if
42 * any) received for this digest pair from the nth voter. */
43 vote_routerstatus_t
*vrs_lst
[FLEXIBLE_ARRAY_MEMBER
];
46 #define ddmap_entry_free(e) \
47 FREE_AND_NULL(ddmap_entry_t, ddmap_entry_free_, (e))
49 /** Release all storage held by e. */
51 ddmap_entry_free_(ddmap_entry_t
*e
)
56 /** Return a new empty ddmap_entry, with <b>n_votes</b> elements in
58 static ddmap_entry_t
*
59 ddmap_entry_new(int n_votes
)
61 return tor_malloc_zero(offsetof(ddmap_entry_t
, vrs_lst
) +
62 sizeof(vote_routerstatus_t
*) * n_votes
);
65 /** Helper: compute a hash of a single ddmap_entry_t's identity (or
68 ddmap_entry_hash(const ddmap_entry_t
*ent
)
70 return (unsigned) siphash24g(ent
->d
, sizeof(ent
->d
));
73 /** Helper: return true if <b>a</b> and <b>b</b> have the same
74 * identity/identities. */
76 ddmap_entry_eq(const ddmap_entry_t
*a
, const ddmap_entry_t
*b
)
78 return fast_memeq(a
->d
, b
->d
, sizeof(a
->d
));
81 /** Record the RSA identity of <b>ent</b> as <b>rsa_sha1</b>, and the
82 * ed25519 identity as <b>ed25519</b>. Both must be provided. */
84 ddmap_entry_set_digests(ddmap_entry_t
*ent
,
85 const uint8_t *rsa_sha1
,
86 const uint8_t *ed25519
)
88 memcpy(ent
->d
, rsa_sha1
, DIGEST_LEN
);
89 memcpy(ent
->d
+ DIGEST_LEN
, ed25519
, DIGEST256_LEN
);
92 HT_PROTOTYPE(double_digest_map
, ddmap_entry_t
, node
, ddmap_entry_hash
,
94 HT_GENERATE2(double_digest_map
, ddmap_entry_t
, node
, ddmap_entry_hash
,
95 ddmap_entry_eq
, 0.6, tor_reallocarray
, tor_free_
);
97 /** Helper: add a single vote_routerstatus_t <b>vrs</b> to the collator
98 * <b>dc</b>, indexing it by its RSA key digest, and by the 2-tuple of its RSA
99 * key digest and Ed25519 key. It must come from the <b>vote_num</b>th
102 * Requires that the vote is well-formed -- that is, that it has no duplicate
103 * routerstatus entries. We already checked for that when parsing the vote. */
105 dircollator_add_routerstatus(dircollator_t
*dc
,
107 networkstatus_t
*vote
,
108 vote_routerstatus_t
*vrs
)
110 const char *id
= vrs
->status
.identity_digest
;
112 /* Clear this flag; we might set it later during the voting process */
113 vrs
->ed25519_reflects_consensus
= 0;
115 (void) vote
; // We don't currently need this.
117 /* First, add this item to the appropriate RSA-SHA-Id array. */
118 vote_routerstatus_t
**vrs_lst
= digestmap_get(dc
->by_rsa_sha1
, id
);
119 if (NULL
== vrs_lst
) {
120 vrs_lst
= tor_calloc(dc
->n_votes
, sizeof(vote_routerstatus_t
*));
121 digestmap_set(dc
->by_rsa_sha1
, id
, vrs_lst
);
123 tor_assert(vrs_lst
[vote_num
] == NULL
);
124 vrs_lst
[vote_num
] = vrs
;
126 const uint8_t *ed
= vrs
->ed25519_id
;
128 if (! vrs
->has_ed25519_listing
)
131 /* Now add it to the appropriate <Ed,RSA-SHA-Id> array. */
132 ddmap_entry_t search
, *found
;
133 memset(&search
, 0, sizeof(search
));
134 ddmap_entry_set_digests(&search
, (const uint8_t *)id
, ed
);
135 found
= HT_FIND(double_digest_map
, &dc
->by_both_ids
, &search
);
137 found
= ddmap_entry_new(dc
->n_votes
);
138 ddmap_entry_set_digests(found
, (const uint8_t *)id
, ed
);
139 HT_INSERT(double_digest_map
, &dc
->by_both_ids
, found
);
141 vrs_lst
= found
->vrs_lst
;
142 tor_assert(vrs_lst
[vote_num
] == NULL
);
143 vrs_lst
[vote_num
] = vrs
;
146 /** Create and return a new dircollator object to use when collating
147 * <b>n_votes</b> out of a total of <b>n_authorities</b>. */
149 dircollator_new(int n_votes
, int n_authorities
)
151 dircollator_t
*dc
= tor_malloc_zero(sizeof(dircollator_t
));
153 tor_assert(n_votes
<= n_authorities
);
155 dc
->n_votes
= n_votes
;
156 dc
->n_authorities
= n_authorities
;
158 dc
->by_rsa_sha1
= digestmap_new();
159 HT_INIT(double_digest_map
, &dc
->by_both_ids
);
164 /** Release all storage held by <b>dc</b>. */
166 dircollator_free_(dircollator_t
*dc
)
171 if (dc
->by_collated_rsa_sha1
!= dc
->by_rsa_sha1
)
172 digestmap_free(dc
->by_collated_rsa_sha1
, NULL
);
174 digestmap_free(dc
->by_rsa_sha1
, tor_free_
);
175 smartlist_free(dc
->all_rsa_sha1_lst
);
177 ddmap_entry_t
**e
, **next
, *this;
178 for (e
= HT_START(double_digest_map
, &dc
->by_both_ids
);
179 e
!= NULL
; e
= next
) {
181 next
= HT_NEXT_RMV(double_digest_map
, &dc
->by_both_ids
, e
);
182 ddmap_entry_free(this);
184 HT_CLEAR(double_digest_map
, &dc
->by_both_ids
);
189 /** Add a single vote <b>v</b> to a dircollator <b>dc</b>. This function must
190 * be called exactly once for each vote to be used in the consensus. It may
191 * only be called before dircollator_collate().
194 dircollator_add_vote(dircollator_t
*dc
, networkstatus_t
*v
)
196 tor_assert(v
->type
== NS_TYPE_VOTE
);
197 tor_assert(dc
->next_vote_num
< dc
->n_votes
);
198 tor_assert(!dc
->is_collated
);
200 const int votenum
= dc
->next_vote_num
++;
202 SMARTLIST_FOREACH_BEGIN(v
->routerstatus_list
, vote_routerstatus_t
*, vrs
) {
203 dircollator_add_routerstatus(dc
, votenum
, v
, vrs
);
204 } SMARTLIST_FOREACH_END(vrs
);
207 /** Sort the entries in <b>dc</b> according to <b>consensus_method</b>, so
208 * that the consensus process can iterate over them with
209 * dircollator_n_routers() and dircollator_get_votes_for_router(). */
211 dircollator_collate(dircollator_t
*dc
, int consensus_method
)
213 (void) consensus_method
;
215 tor_assert(!dc
->is_collated
);
216 dc
->all_rsa_sha1_lst
= smartlist_new();
218 dircollator_collate_by_ed25519(dc
);
220 smartlist_sort_digests(dc
->all_rsa_sha1_lst
);
225 * Collation function for ed25519 consensuses: collate the votes for each
226 * entry in <b>dc</b> by ed25519 key and by RSA key.
228 * The rule is, approximately:
229 * If a (ed,rsa) identity is listed by more than half of authorities,
230 * include it. And include all (rsa)-only votes about that node as
233 * Otherwise, if an (*,rsa) or (rsa) identity is listed by more than
234 * half of the authorities, and no (ed,rsa) pair for the same RSA key
235 * has been already been included based on the rule above, include
239 dircollator_collate_by_ed25519(dircollator_t
*dc
)
241 const int total_authorities
= dc
->n_authorities
;
242 digestmap_t
*rsa_digests
= digestmap_new();
244 ddmap_entry_t
**iter
;
246 /* Go over all <ed,rsa> pairs */
247 HT_FOREACH(iter
, double_digest_map
, &dc
->by_both_ids
) {
248 ddmap_entry_t
*ent
= *iter
;
250 for (i
= 0; i
< dc
->n_votes
; ++i
) {
251 if (ent
->vrs_lst
[i
] != NULL
)
255 /* If not enough authorties listed this exact <ed,rsa> pair,
256 * don't include it. */
257 if (n
<= total_authorities
/ 2)
260 /* Now consider whether there are any other entries with the same
261 * RSA key (but with possibly different or missing ed value). */
262 vote_routerstatus_t
**vrs_lst2
= digestmap_get(dc
->by_rsa_sha1
,
264 tor_assert(vrs_lst2
);
266 for (i
= 0; i
< dc
->n_votes
; ++i
) {
267 if (ent
->vrs_lst
[i
] != NULL
) {
268 ent
->vrs_lst
[i
]->ed25519_reflects_consensus
= 1;
269 } else if (vrs_lst2
[i
] && ! vrs_lst2
[i
]->has_ed25519_listing
) {
270 ent
->vrs_lst
[i
] = vrs_lst2
[i
];
274 /* Record that we have seen this RSA digest. */
275 digestmap_set(rsa_digests
, (char*)ent
->d
, ent
->vrs_lst
);
276 smartlist_add(dc
->all_rsa_sha1_lst
, ent
->d
);
279 /* Now look over all entries with an RSA digest, looking for RSA digests
280 * we didn't put in yet.
282 DIGESTMAP_FOREACH(dc
->by_rsa_sha1
, k
, vote_routerstatus_t
**, vrs_lst
) {
283 if (digestmap_get(rsa_digests
, k
) != NULL
)
284 continue; /* We already included this RSA digest */
287 for (i
= 0; i
< dc
->n_votes
; ++i
) {
288 if (vrs_lst
[i
] != NULL
)
292 if (n
<= total_authorities
/ 2)
293 continue; /* Not enough votes */
295 digestmap_set(rsa_digests
, k
, vrs_lst
);
296 smartlist_add(dc
->all_rsa_sha1_lst
, (char *)k
);
297 } DIGESTMAP_FOREACH_END
;
299 dc
->by_collated_rsa_sha1
= rsa_digests
;
302 /** Return the total number of collated router entries. This function may
303 * only be called after dircollator_collate. */
305 dircollator_n_routers(dircollator_t
*dc
)
307 tor_assert(dc
->is_collated
);
308 return smartlist_len(dc
->all_rsa_sha1_lst
);
311 /** Return an array of vote_routerstatus_t entries for the <b>idx</b>th router
312 * in the collation order. Each array contains n_votes elements, where the
313 * nth element of the array is the vote_routerstatus_t from the nth voter for
314 * this identity (or NULL if there is no such entry).
316 * The maximum value for <b>idx</b> is dircollator_n_routers().
318 * This function may only be called after dircollator_collate. */
319 vote_routerstatus_t
**
320 dircollator_get_votes_for_router(dircollator_t
*dc
, int idx
)
322 tor_assert(dc
->is_collated
);
323 tor_assert(idx
< smartlist_len(dc
->all_rsa_sha1_lst
));
324 return digestmap_get(dc
->by_collated_rsa_sha1
,
325 smartlist_get(dc
->all_rsa_sha1_lst
, idx
));