mirror of
https://gitlab.torproject.org/tpo/core/tor.git
synced 2024-11-10 13:13:44 +01:00
Merge remote-tracking branch 'dgoulet/ticket16943_029_05-squashed'
Trivial Conflicts: src/or/or.h src/or/routerparse.c
This commit is contained in:
commit
aaa3129043
8
changes/bug16943
Normal file
8
changes/bug16943
Normal file
@ -0,0 +1,8 @@
|
||||
o Major features (dirauths, security, hidden services):
|
||||
- Directory authorities can now perform the shared randomness protocol
|
||||
specified by proposal 250. Using this protocol, directory authorities can
|
||||
generate a global fresh random number every day. In the future, this
|
||||
global randomness will be used by hidden services to select their
|
||||
responsible HSDirs. This release only implements the directory authority
|
||||
feature; the hidden service side will be implemented in the future as
|
||||
part of proposal 224 . Resolves ticket #16943 and proposal 250.
|
@ -2239,6 +2239,12 @@ on the public Tor network.
|
||||
in a journal if it is new, or if it differs from the most recently
|
||||
accepted pinning for one of the keys it contains. (Default: 0)
|
||||
|
||||
[[AuthDirSharedRandomness]] **AuthDirSharedRandomness** **0**|**1**::
|
||||
Authoritative directories only. Switch for the shared random protocol.
|
||||
If zero, the authority won't participate in the protocol. If non-zero
|
||||
(default), the flag "shared-rand-participate" is added to the authority
|
||||
vote indicating participation in the protocol. (Default: 1)
|
||||
|
||||
[[BridgePassword]] **BridgePassword** __Password__::
|
||||
If set, contains an HTTP authenticator that tells a bridge authority to
|
||||
serve all requested bridge information. Used by the (only partially
|
||||
|
@ -5687,3 +5687,24 @@ clamp_double_to_int64(double number)
|
||||
return signbit(number) ? INT64_MIN : INT64_MAX;
|
||||
}
|
||||
|
||||
/** Return a uint64_t value from <b>a</b> in network byte order. */
|
||||
uint64_t
|
||||
tor_htonll(uint64_t a)
|
||||
{
|
||||
#ifdef WORDS_BIGENDIAN
|
||||
/* Big endian. */
|
||||
return a;
|
||||
#else /* WORDS_BIGENDIAN */
|
||||
/* Little endian. The worst... */
|
||||
return htonl((uint32_t)(a>>32)) |
|
||||
(((uint64_t)htonl((uint32_t)a))<<32);
|
||||
#endif /* WORDS_BIGENDIAN */
|
||||
}
|
||||
|
||||
/** Return a uint64_t value from <b>a</b> in host byte order. */
|
||||
uint64_t
|
||||
tor_ntohll(uint64_t a)
|
||||
{
|
||||
return tor_htonll(a);
|
||||
}
|
||||
|
||||
|
@ -61,6 +61,8 @@ void *tor_memdup_(const void *mem, size_t len DMALLOC_PARAMS)
|
||||
void *tor_memdup_nulterm_(const void *mem, size_t len DMALLOC_PARAMS)
|
||||
ATTR_MALLOC ATTR_NONNULL((1));
|
||||
void tor_free_(void *mem);
|
||||
uint64_t tor_htonll(uint64_t a);
|
||||
uint64_t tor_ntohll(uint64_t a);
|
||||
#ifdef USE_DMALLOC
|
||||
extern int dmalloc_free(const char *file, const int line, void *pnt,
|
||||
const int func_id);
|
||||
|
@ -440,6 +440,7 @@ static config_var_t option_vars_[] = {
|
||||
V(UseNTorHandshake, AUTOBOOL, "1"),
|
||||
V(User, STRING, NULL),
|
||||
V(UserspaceIOCPBuffers, BOOL, "0"),
|
||||
V(AuthDirSharedRandomness, BOOL, "1"),
|
||||
OBSOLETE("V1AuthoritativeDirectory"),
|
||||
OBSOLETE("V2AuthoritativeDirectory"),
|
||||
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include "routerlist.h"
|
||||
#include "routerparse.h"
|
||||
#include "routerset.h"
|
||||
#include "shared_random.h"
|
||||
|
||||
#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
|
||||
#ifndef OPENBSD
|
||||
@ -2026,6 +2027,10 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
|
||||
update_microdescs_from_networkstatus(now);
|
||||
update_microdesc_downloads(now);
|
||||
directory_info_has_arrived(now, 0, 0);
|
||||
if (authdir_mode_v3(get_options())) {
|
||||
sr_act_post_consensus(
|
||||
networkstatus_get_latest_consensus_by_flavor(FLAV_NS));
|
||||
}
|
||||
log_info(LD_DIR, "Successfully loaded consensus.");
|
||||
}
|
||||
|
||||
|
179
src/or/dirvote.c
179
src/or/dirvote.c
@ -15,10 +15,12 @@
|
||||
#include "policies.h"
|
||||
#include "rephist.h"
|
||||
#include "router.h"
|
||||
#include "routerkeys.h"
|
||||
#include "routerlist.h"
|
||||
#include "routerparse.h"
|
||||
#include "entrynodes.h" /* needed for guardfraction methods */
|
||||
#include "torcert.h"
|
||||
#include "shared_random_state.h"
|
||||
|
||||
/**
|
||||
* \file dirvote.c
|
||||
@ -73,6 +75,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
char digest[DIGEST_LEN];
|
||||
uint32_t addr;
|
||||
char *client_versions_line = NULL, *server_versions_line = NULL;
|
||||
char *shared_random_vote_str = NULL;
|
||||
networkstatus_voter_info_t *voter;
|
||||
char *status = NULL;
|
||||
|
||||
@ -114,6 +117,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
packages = tor_strdup("");
|
||||
}
|
||||
|
||||
/* Get shared random commitments/reveals line(s). */
|
||||
shared_random_vote_str = sr_get_string_for_vote();
|
||||
|
||||
{
|
||||
char published[ISO_TIME_LEN+1];
|
||||
char va[ISO_TIME_LEN+1];
|
||||
@ -153,7 +159,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
"flag-thresholds %s\n"
|
||||
"params %s\n"
|
||||
"dir-source %s %s %s %s %d %d\n"
|
||||
"contact %s\n",
|
||||
"contact %s\n"
|
||||
"%s", /* shared randomness information */
|
||||
v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion",
|
||||
methods,
|
||||
published, va, fu, vu,
|
||||
@ -166,12 +173,15 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
|
||||
params,
|
||||
voter->nickname, fingerprint, voter->address,
|
||||
fmt_addr32(addr), voter->dir_port, voter->or_port,
|
||||
voter->contact);
|
||||
voter->contact,
|
||||
shared_random_vote_str ?
|
||||
shared_random_vote_str : "");
|
||||
|
||||
tor_free(params);
|
||||
tor_free(flags);
|
||||
tor_free(flag_thresholds);
|
||||
tor_free(methods);
|
||||
tor_free(shared_random_vote_str);
|
||||
|
||||
if (!tor_digest_is_zero(voter->legacy_id_digest)) {
|
||||
char fpbuf[HEX_DIGEST_LEN+1];
|
||||
@ -608,15 +618,47 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning)
|
||||
return result;
|
||||
}
|
||||
|
||||
/** Given a list of K=V values, return the int32_t value corresponding to
|
||||
* KEYWORD=, or default_val if no such value exists, or if the value is
|
||||
* corrupt.
|
||||
*/
|
||||
STATIC int32_t
|
||||
dirvote_get_intermediate_param_value(const smartlist_t *param_list,
|
||||
const char *keyword,
|
||||
int32_t default_val)
|
||||
{
|
||||
unsigned int n_found = 0;
|
||||
int32_t value = default_val;
|
||||
|
||||
SMARTLIST_FOREACH_BEGIN(param_list, const char *, k_v_pair) {
|
||||
if (!strcmpstart(k_v_pair, keyword) && k_v_pair[strlen(keyword)] == '=') {
|
||||
const char *integer_str = &k_v_pair[strlen(keyword)+1];
|
||||
int ok;
|
||||
value = (int32_t)
|
||||
tor_parse_long(integer_str, 10, INT32_MIN, INT32_MAX, &ok, NULL);
|
||||
if (BUG(! ok))
|
||||
return default_val;
|
||||
++n_found;
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(k_v_pair);
|
||||
|
||||
if (n_found == 1)
|
||||
return value;
|
||||
else if (BUG(n_found > 1))
|
||||
return default_val;
|
||||
else
|
||||
return default_val;
|
||||
}
|
||||
|
||||
/** Minimum number of directory authorities voting for a parameter to
|
||||
* include it in the consensus, if consensus method 12 or later is to be
|
||||
* used. See proposal 178 for details. */
|
||||
#define MIN_VOTES_FOR_PARAM 3
|
||||
|
||||
/** Helper: given a list of valid networkstatus_t, return a new string
|
||||
/** Helper: given a list of valid networkstatus_t, return a new smartlist
|
||||
* containing the contents of the consensus network parameter set.
|
||||
*/
|
||||
STATIC char *
|
||||
STATIC smartlist_t *
|
||||
dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
|
||||
{
|
||||
int i;
|
||||
@ -625,7 +667,6 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
|
||||
int cur_param_len;
|
||||
const char *cur_param;
|
||||
const char *eq;
|
||||
char *result;
|
||||
|
||||
const int n_votes = smartlist_len(votes);
|
||||
smartlist_t *output;
|
||||
@ -647,8 +688,7 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
|
||||
|
||||
if (smartlist_len(param_list) == 0) {
|
||||
tor_free(vals);
|
||||
smartlist_free(param_list);
|
||||
return NULL;
|
||||
return param_list;
|
||||
}
|
||||
|
||||
smartlist_sort_strings(param_list);
|
||||
@ -696,12 +736,9 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(param);
|
||||
|
||||
result = smartlist_join_strings(output, " ", 0, NULL);
|
||||
SMARTLIST_FOREACH(output, char *, cp, tor_free(cp));
|
||||
smartlist_free(output);
|
||||
smartlist_free(param_list);
|
||||
tor_free(vals);
|
||||
return result;
|
||||
return output;
|
||||
}
|
||||
|
||||
#define RANGE_CHECK(a,b,c,d,e,f,g,mx) \
|
||||
@ -1148,6 +1185,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
char *packages = NULL;
|
||||
int added_weights = 0;
|
||||
dircollator_t *collator = NULL;
|
||||
smartlist_t *param_list = NULL;
|
||||
|
||||
tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC);
|
||||
tor_assert(total_authorities >= smartlist_len(votes));
|
||||
tor_assert(total_authorities > 0);
|
||||
@ -1292,14 +1331,31 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
tor_free(flaglist);
|
||||
}
|
||||
|
||||
params = dirvote_compute_params(votes, consensus_method,
|
||||
param_list = dirvote_compute_params(votes, consensus_method,
|
||||
total_authorities);
|
||||
if (params) {
|
||||
if (smartlist_len(param_list)) {
|
||||
params = smartlist_join_strings(param_list, " ", 0, NULL);
|
||||
smartlist_add(chunks, tor_strdup("params "));
|
||||
smartlist_add(chunks, params);
|
||||
smartlist_add(chunks, tor_strdup("\n"));
|
||||
}
|
||||
|
||||
if (consensus_method >= MIN_METHOD_FOR_SHARED_RANDOM) {
|
||||
int num_dirauth = get_n_authorities(V3_DIRINFO);
|
||||
/* Default value of this is 2/3 of the total number of authorities. For
|
||||
* instance, if we have 9 dirauth, the default value is 6. The following
|
||||
* calculation will round it down. */
|
||||
int32_t num_srv_agreements =
|
||||
dirvote_get_intermediate_param_value(param_list,
|
||||
"AuthDirNumSRVAgreements",
|
||||
(num_dirauth * 2) / 3);
|
||||
/* Add the shared random value. */
|
||||
char *srv_lines = sr_get_string_for_consensus(votes, num_srv_agreements);
|
||||
if (srv_lines != NULL) {
|
||||
smartlist_add(chunks, srv_lines);
|
||||
}
|
||||
}
|
||||
|
||||
/* Sort the votes. */
|
||||
smartlist_sort(votes, compare_votes_by_authority_id_);
|
||||
/* Add the authority sections. */
|
||||
@ -1351,7 +1407,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
|
||||
if (consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW) {
|
||||
char *max_unmeasured_param = NULL;
|
||||
/* XXXX Extract this code into a common function */
|
||||
/* XXXX Extract this code into a common function. Or don't! see #19011 */
|
||||
if (params) {
|
||||
if (strcmpstart(params, "maxunmeasuredbw=") == 0)
|
||||
max_unmeasured_param = params;
|
||||
@ -1906,7 +1962,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
// Parse params, extract BW_WEIGHT_SCALE if present
|
||||
// DO NOT use consensus_param_bw_weight_scale() in this code!
|
||||
// The consensus is not formed yet!
|
||||
/* XXXX Extract this code into a common function */
|
||||
/* XXXX Extract this code into a common function. Or not: #19011. */
|
||||
if (params) {
|
||||
if (strcmpstart(params, "bwweightscale=") == 0)
|
||||
bw_weight_param = params;
|
||||
@ -2026,6 +2082,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
|
||||
smartlist_free(flags);
|
||||
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
|
||||
smartlist_free(chunks);
|
||||
SMARTLIST_FOREACH(param_list, char *, cp, tor_free(cp));
|
||||
smartlist_free(param_list);
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -2511,50 +2569,60 @@ dirvote_get_start_of_next_interval(time_t now, int interval, int offset)
|
||||
return next;
|
||||
}
|
||||
|
||||
/** Scheduling information for a voting interval. */
|
||||
static struct {
|
||||
/** When do we generate and distribute our vote for this interval? */
|
||||
time_t voting_starts;
|
||||
/** When do we send an HTTP request for any votes that we haven't
|
||||
* been posted yet?*/
|
||||
time_t fetch_missing_votes;
|
||||
/** When do we give up on getting more votes and generate a consensus? */
|
||||
time_t voting_ends;
|
||||
/** When do we send an HTTP request for any signatures we're expecting to
|
||||
* see on the consensus? */
|
||||
time_t fetch_missing_signatures;
|
||||
/** When do we publish the consensus? */
|
||||
time_t interval_starts;
|
||||
/* Using the time <b>now</b>, return the next voting valid-after time. */
|
||||
time_t
|
||||
get_next_valid_after_time(time_t now)
|
||||
{
|
||||
time_t next_valid_after_time;
|
||||
const or_options_t *options = get_options();
|
||||
voting_schedule_t *new_voting_schedule =
|
||||
get_voting_schedule(options, now, LOG_INFO);
|
||||
tor_assert(new_voting_schedule);
|
||||
|
||||
/* True iff we have generated and distributed our vote. */
|
||||
int have_voted;
|
||||
/* True iff we've requested missing votes. */
|
||||
int have_fetched_missing_votes;
|
||||
/* True iff we have built a consensus and sent the signatures around. */
|
||||
int have_built_consensus;
|
||||
/* True iff we've fetched missing signatures. */
|
||||
int have_fetched_missing_signatures;
|
||||
/* True iff we have published our consensus. */
|
||||
int have_published_consensus;
|
||||
} voting_schedule = {0,0,0,0,0,0,0,0,0,0};
|
||||
next_valid_after_time = new_voting_schedule->interval_starts;
|
||||
tor_free(new_voting_schedule);
|
||||
|
||||
return next_valid_after_time;
|
||||
}
|
||||
|
||||
static voting_schedule_t voting_schedule;
|
||||
|
||||
/** Set voting_schedule to hold the timing for the next vote we should be
|
||||
* doing. */
|
||||
void
|
||||
dirvote_recalculate_timing(const or_options_t *options, time_t now)
|
||||
{
|
||||
voting_schedule_t *new_voting_schedule;
|
||||
|
||||
if (!authdir_mode_v3(options)) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* get the new voting schedule */
|
||||
new_voting_schedule = get_voting_schedule(options, now, LOG_NOTICE);
|
||||
tor_assert(new_voting_schedule);
|
||||
|
||||
/* Fill in the global static struct now */
|
||||
memcpy(&voting_schedule, new_voting_schedule, sizeof(voting_schedule));
|
||||
tor_free(new_voting_schedule);
|
||||
}
|
||||
|
||||
/* Populate and return a new voting_schedule_t that can be used to schedule
|
||||
* voting. The object is allocated on the heap and it's the responsibility of
|
||||
* the caller to free it. Can't fail. */
|
||||
voting_schedule_t *
|
||||
get_voting_schedule(const or_options_t *options, time_t now, int severity)
|
||||
{
|
||||
int interval, vote_delay, dist_delay;
|
||||
time_t start;
|
||||
time_t end;
|
||||
networkstatus_t *consensus;
|
||||
voting_schedule_t *new_voting_schedule;
|
||||
|
||||
if (!authdir_mode_v3(options))
|
||||
return;
|
||||
new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t));
|
||||
|
||||
consensus = networkstatus_get_live_consensus(now);
|
||||
|
||||
memset(&voting_schedule, 0, sizeof(voting_schedule));
|
||||
|
||||
if (consensus) {
|
||||
interval = (int)( consensus->fresh_until - consensus->valid_after );
|
||||
vote_delay = consensus->vote_seconds;
|
||||
@ -2570,7 +2638,7 @@ dirvote_recalculate_timing(const or_options_t *options, time_t now)
|
||||
if (vote_delay + dist_delay > interval/2)
|
||||
vote_delay = dist_delay = interval / 4;
|
||||
|
||||
start = voting_schedule.interval_starts =
|
||||
start = new_voting_schedule->interval_starts =
|
||||
dirvote_get_start_of_next_interval(now,interval,
|
||||
options->TestingV3AuthVotingStartOffset);
|
||||
end = dirvote_get_start_of_next_interval(start+1, interval,
|
||||
@ -2578,18 +2646,20 @@ dirvote_recalculate_timing(const or_options_t *options, time_t now)
|
||||
|
||||
tor_assert(end > start);
|
||||
|
||||
voting_schedule.fetch_missing_signatures = start - (dist_delay/2);
|
||||
voting_schedule.voting_ends = start - dist_delay;
|
||||
voting_schedule.fetch_missing_votes = start - dist_delay - (vote_delay/2);
|
||||
voting_schedule.voting_starts = start - dist_delay - vote_delay;
|
||||
new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2);
|
||||
new_voting_schedule->voting_ends = start - dist_delay;
|
||||
new_voting_schedule->fetch_missing_votes = start - dist_delay - (vote_delay/2);
|
||||
new_voting_schedule->voting_starts = start - dist_delay - vote_delay;
|
||||
|
||||
{
|
||||
char tbuf[ISO_TIME_LEN+1];
|
||||
format_iso_time(tbuf, voting_schedule.interval_starts);
|
||||
log_notice(LD_DIR,"Choosing expected valid-after time as %s: "
|
||||
format_iso_time(tbuf, new_voting_schedule->interval_starts);
|
||||
tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: "
|
||||
"consensus_set=%d, interval=%d",
|
||||
tbuf, consensus?1:0, interval);
|
||||
}
|
||||
|
||||
return new_voting_schedule;
|
||||
}
|
||||
|
||||
/** Entry point: Take whatever voting actions are pending as of <b>now</b>. */
|
||||
@ -2638,6 +2708,9 @@ dirvote_act(const or_options_t *options, time_t now)
|
||||
dirvote_publish_consensus();
|
||||
dirvote_clear_votes(0);
|
||||
voting_schedule.have_published_consensus = 1;
|
||||
/* Update our shared random state with the consensus just published. */
|
||||
sr_act_post_consensus(
|
||||
networkstatus_get_latest_consensus_by_flavor(FLAV_NS));
|
||||
/* XXXX We will want to try again later if we haven't got enough
|
||||
* signatures yet. Implement this if it turns out to ever happen. */
|
||||
dirvote_recalculate_timing(options, now);
|
||||
@ -2977,6 +3050,10 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
|
||||
}
|
||||
} SMARTLIST_FOREACH_END(v);
|
||||
|
||||
/* This a valid vote, update our shared random state. */
|
||||
sr_handle_received_commits(vote->sr_info.commits,
|
||||
vote->cert->identity_key);
|
||||
|
||||
pending_vote = tor_malloc_zero(sizeof(pending_vote_t));
|
||||
pending_vote->vote_body = new_cached_dir(tor_strndup(vote_body,
|
||||
end_of_vote-vote_body),
|
||||
|
@ -55,7 +55,7 @@
|
||||
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
|
||||
|
||||
/** The highest consensus method that we currently support. */
|
||||
#define MAX_SUPPORTED_CONSENSUS_METHOD 22
|
||||
#define MAX_SUPPORTED_CONSENSUS_METHOD 23
|
||||
|
||||
/** Lowest consensus method where microdesc consensuses omit any entry
|
||||
* with no microdesc. */
|
||||
@ -90,10 +90,15 @@
|
||||
* ed25519 identities in microdescriptors. (Broken; see
|
||||
* consensus_method_is_supported() for more info.) */
|
||||
#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21
|
||||
|
||||
/** Lowest consensus method where authorities vote on ed25519 ids and ensure
|
||||
* ed25519 id consistency. */
|
||||
#define MIN_METHOD_FOR_ED25519_ID_VOTING 22
|
||||
|
||||
/** Lowest consensus method where authorities may include a shared random
|
||||
* value(s). */
|
||||
#define MIN_METHOD_FOR_SHARED_RANDOM 23
|
||||
|
||||
/** Default bandwidth to clip unmeasured bandwidths to using method >=
|
||||
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
|
||||
* get confused with the above macros.) */
|
||||
@ -121,12 +126,44 @@ void ns_detached_signatures_free(ns_detached_signatures_t *s);
|
||||
authority_cert_t *authority_cert_dup(authority_cert_t *cert);
|
||||
|
||||
/* vote scheduling */
|
||||
|
||||
/** Scheduling information for a voting interval. */
|
||||
typedef struct {
|
||||
/** When do we generate and distribute our vote for this interval? */
|
||||
time_t voting_starts;
|
||||
/** When do we send an HTTP request for any votes that we haven't
|
||||
* been posted yet?*/
|
||||
time_t fetch_missing_votes;
|
||||
/** When do we give up on getting more votes and generate a consensus? */
|
||||
time_t voting_ends;
|
||||
/** When do we send an HTTP request for any signatures we're expecting to
|
||||
* see on the consensus? */
|
||||
time_t fetch_missing_signatures;
|
||||
/** When do we publish the consensus? */
|
||||
time_t interval_starts;
|
||||
|
||||
/* True iff we have generated and distributed our vote. */
|
||||
int have_voted;
|
||||
/* True iff we've requested missing votes. */
|
||||
int have_fetched_missing_votes;
|
||||
/* True iff we have built a consensus and sent the signatures around. */
|
||||
int have_built_consensus;
|
||||
/* True iff we've fetched missing signatures. */
|
||||
int have_fetched_missing_signatures;
|
||||
/* True iff we have published our consensus. */
|
||||
int have_published_consensus;
|
||||
} voting_schedule_t;
|
||||
|
||||
voting_schedule_t *get_voting_schedule(const or_options_t *options,
|
||||
time_t now, int severity);
|
||||
|
||||
void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out);
|
||||
time_t dirvote_get_start_of_next_interval(time_t now,
|
||||
int interval,
|
||||
int offset);
|
||||
void dirvote_recalculate_timing(const or_options_t *options, time_t now);
|
||||
void dirvote_act(const or_options_t *options, time_t now);
|
||||
time_t get_next_valid_after_time(time_t now);
|
||||
|
||||
/* invoked on timers and by outside triggers. */
|
||||
struct pending_vote_t * dirvote_add_vote(const char *vote_body,
|
||||
@ -173,9 +210,13 @@ document_signature_t *voter_get_sig_by_algorithm(
|
||||
digest_algorithm_t alg);
|
||||
|
||||
#ifdef DIRVOTE_PRIVATE
|
||||
STATIC int32_t dirvote_get_intermediate_param_value(
|
||||
const smartlist_t *param_list,
|
||||
const char *keyword,
|
||||
int32_t default_val);
|
||||
STATIC char *format_networkstatus_vote(crypto_pk_t *private_key,
|
||||
networkstatus_t *v3_ns);
|
||||
STATIC char *dirvote_compute_params(smartlist_t *votes, int method,
|
||||
STATIC smartlist_t *dirvote_compute_params(smartlist_t *votes, int method,
|
||||
int total_authorities);
|
||||
STATIC char *compute_consensus_package_lines(smartlist_t *votes);
|
||||
STATIC char *make_consensus_method_list(int low, int high, const char *sep);
|
||||
|
@ -62,6 +62,8 @@ LIBTOR_A_SOURCES = \
|
||||
src/or/onion.c \
|
||||
src/or/onion_fast.c \
|
||||
src/or/onion_tap.c \
|
||||
src/or/shared_random.c \
|
||||
src/or/shared_random_state.c \
|
||||
src/or/transports.c \
|
||||
src/or/periodic.c \
|
||||
src/or/policies.c \
|
||||
@ -173,6 +175,8 @@ ORHEADERS = \
|
||||
src/or/onion_ntor.h \
|
||||
src/or/onion_tap.h \
|
||||
src/or/or.h \
|
||||
src/or/shared_random.h \
|
||||
src/or/shared_random_state.h \
|
||||
src/or/transports.h \
|
||||
src/or/periodic.h \
|
||||
src/or/policies.h \
|
||||
|
@ -57,6 +57,7 @@
|
||||
#include "routerlist.h"
|
||||
#include "routerparse.h"
|
||||
#include "scheduler.h"
|
||||
#include "shared_random.h"
|
||||
#include "statefile.h"
|
||||
#include "status.h"
|
||||
#include "util_process.h"
|
||||
@ -2447,6 +2448,13 @@ do_main_loop(void)
|
||||
cpu_init();
|
||||
}
|
||||
|
||||
/* Setup shared random protocol subsystem. */
|
||||
if (authdir_mode_publishes_statuses(get_options())) {
|
||||
if (sr_init(1) < 0) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/* set up once-a-second callback. */
|
||||
if (! second_timer) {
|
||||
struct timeval one_second;
|
||||
@ -3214,6 +3222,9 @@ tor_cleanup(void)
|
||||
accounting_record_bandwidth_usage(now, get_or_state());
|
||||
or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */
|
||||
or_state_save(now);
|
||||
if (authdir_mode(options)) {
|
||||
sr_save_and_cleanup();
|
||||
}
|
||||
if (authdir_mode_tests_reachability(options))
|
||||
rep_hist_record_mtbf_data(now, 0);
|
||||
keypin_close_journal();
|
||||
@ -3372,6 +3383,7 @@ sandbox_init_filter(void)
|
||||
OPEN_DATADIR_SUFFIX("cached-extrainfo.new", ".tmp");
|
||||
OPEN_DATADIR("cached-extrainfo.tmp.tmp");
|
||||
OPEN_DATADIR_SUFFIX("state", ".tmp");
|
||||
OPEN_DATADIR_SUFFIX("sr-state", ".tmp");
|
||||
OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp");
|
||||
OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp");
|
||||
OPEN_DATADIR("key-pinning-journal");
|
||||
@ -3424,6 +3436,7 @@ sandbox_init_filter(void)
|
||||
RENAME_SUFFIX("cached-extrainfo", ".new");
|
||||
RENAME_SUFFIX("cached-extrainfo.new", ".tmp");
|
||||
RENAME_SUFFIX("state", ".tmp");
|
||||
RENAME_SUFFIX("sr-state", ".tmp");
|
||||
RENAME_SUFFIX("unparseable-desc", ".tmp");
|
||||
RENAME_SUFFIX("v3-status-votes", ".tmp");
|
||||
|
||||
|
@ -32,7 +32,9 @@
|
||||
#include "router.h"
|
||||
#include "routerlist.h"
|
||||
#include "routerparse.h"
|
||||
#include "shared_random.h"
|
||||
#include "transports.h"
|
||||
#include "torcert.h"
|
||||
|
||||
/** Map from lowercase nickname to identity digest of named server, if any. */
|
||||
static strmap_t *named_server_map = NULL;
|
||||
@ -320,6 +322,14 @@ networkstatus_vote_free(networkstatus_t *ns)
|
||||
|
||||
digestmap_free(ns->desc_digest_map, NULL);
|
||||
|
||||
if (ns->sr_info.commits) {
|
||||
SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c,
|
||||
sr_commit_free(c));
|
||||
smartlist_free(ns->sr_info.commits);
|
||||
}
|
||||
tor_free(ns->sr_info.previous_srv);
|
||||
tor_free(ns->sr_info.current_srv);
|
||||
|
||||
memwipe(ns, 11, sizeof(*ns));
|
||||
tor_free(ns);
|
||||
}
|
||||
@ -1264,8 +1274,8 @@ networkstatus_get_dl_status_by_flavor_running,(consensus_flavor_t flavor))
|
||||
|
||||
/** Return the most recent consensus that we have downloaded, or NULL if we
|
||||
* don't have one. */
|
||||
networkstatus_t *
|
||||
networkstatus_get_latest_consensus(void)
|
||||
MOCK_IMPL(networkstatus_t *,
|
||||
networkstatus_get_latest_consensus,(void))
|
||||
{
|
||||
return current_consensus;
|
||||
}
|
||||
@ -1287,8 +1297,8 @@ networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f))
|
||||
|
||||
/** Return the most recent consensus that we have downloaded, or NULL if it is
|
||||
* no longer live. */
|
||||
networkstatus_t *
|
||||
networkstatus_get_live_consensus(time_t now)
|
||||
MOCK_IMPL(networkstatus_t *,
|
||||
networkstatus_get_live_consensus,(time_t now))
|
||||
{
|
||||
if (current_consensus &&
|
||||
current_consensus->valid_after <= now &&
|
||||
|
@ -75,10 +75,10 @@ void update_certificate_downloads(time_t now);
|
||||
int consensus_is_waiting_for_certs(void);
|
||||
int client_would_use_router(const routerstatus_t *rs, time_t now,
|
||||
const or_options_t *options);
|
||||
networkstatus_t *networkstatus_get_latest_consensus(void);
|
||||
MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus,(void));
|
||||
MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
|
||||
(consensus_flavor_t f));
|
||||
networkstatus_t *networkstatus_get_live_consensus(time_t now);
|
||||
MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now));
|
||||
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
|
||||
int flavor);
|
||||
MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now));
|
||||
|
21
src/or/or.h
21
src/or/or.h
@ -2523,6 +2523,18 @@ typedef struct networkstatus_voter_info_t {
|
||||
smartlist_t *sigs;
|
||||
} networkstatus_voter_info_t;
|
||||
|
||||
typedef struct networkstatus_sr_info_t {
|
||||
/* Indicate if the dirauth partitipates in the SR protocol with its vote.
|
||||
* This is tied to the SR flag in the vote. */
|
||||
unsigned int participate:1;
|
||||
/* Both vote and consensus: Current and previous SRV. If list is empty,
|
||||
* this means none were found in either the consensus or vote. */
|
||||
struct sr_srv_t *previous_srv;
|
||||
struct sr_srv_t *current_srv;
|
||||
/* Vote only: List of commitments. */
|
||||
smartlist_t *commits;
|
||||
} networkstatus_sr_info_t;
|
||||
|
||||
/** Enumerates the possible seriousness values of a networkstatus document. */
|
||||
typedef enum {
|
||||
NS_TYPE_VOTE,
|
||||
@ -2605,6 +2617,9 @@ typedef struct networkstatus_t {
|
||||
/** If present, a map from descriptor digest to elements of
|
||||
* routerstatus_list. */
|
||||
digestmap_t *desc_digest_map;
|
||||
|
||||
/** Contains the shared random protocol data from a vote or consensus. */
|
||||
networkstatus_sr_info_t sr_info;
|
||||
} networkstatus_t;
|
||||
|
||||
/** A set of signatures for a networkstatus consensus. Unless otherwise
|
||||
@ -4503,6 +4518,12 @@ typedef struct {
|
||||
* lifetime of this Tor process.
|
||||
*/
|
||||
uint64_t MaxUnparseableDescSizeToLog;
|
||||
|
||||
/** Bool (default: 1): Switch for the shared random protocol. Only
|
||||
* relevant to a directory authority. If off, the authority won't
|
||||
* participate in the protocol. If on (default), a flag is added to the
|
||||
* vote indicating participation. */
|
||||
int AuthDirSharedRandomness;
|
||||
} or_options_t;
|
||||
|
||||
/** Persistent state for an onion router, as saved to disk. */
|
||||
|
@ -1645,8 +1645,8 @@ router_digest_is_fallback_dir(const char *digest)
|
||||
* v3 identity key hashes to <b>digest</b>, or NULL if no such authority
|
||||
* is known.
|
||||
*/
|
||||
dir_server_t *
|
||||
trusteddirserver_get_by_v3_auth_digest(const char *digest)
|
||||
MOCK_IMPL(dir_server_t *,
|
||||
trusteddirserver_get_by_v3_auth_digest, (const char *digest))
|
||||
{
|
||||
if (!trusted_dir_servers)
|
||||
return NULL;
|
||||
|
@ -52,7 +52,8 @@ dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
|
||||
dir_server_t *router_get_fallback_dirserver_by_digest(
|
||||
const char *digest);
|
||||
int router_digest_is_fallback_dir(const char *digest);
|
||||
dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
|
||||
MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest,
|
||||
(const char *d));
|
||||
const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
|
||||
int flags);
|
||||
const routerstatus_t *router_pick_fallback_dirserver(dirinfo_type_t type,
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include "entrynodes.h"
|
||||
#include "torcert.h"
|
||||
#include "sandbox.h"
|
||||
#include "shared_random.h"
|
||||
|
||||
#undef log
|
||||
#include <math.h>
|
||||
@ -146,6 +147,11 @@ typedef enum {
|
||||
K_CONSENSUS_METHOD,
|
||||
K_LEGACY_DIR_KEY,
|
||||
K_DIRECTORY_FOOTER,
|
||||
K_SIGNING_CERT_ED,
|
||||
K_SR_FLAG,
|
||||
K_COMMIT,
|
||||
K_PREVIOUS_SRV,
|
||||
K_CURRENT_SRV,
|
||||
K_PACKAGE,
|
||||
|
||||
A_PURPOSE,
|
||||
@ -447,6 +453,11 @@ static token_rule_t networkstatus_token_table[] = {
|
||||
T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ),
|
||||
T01("params", K_PARAMS, ARGS, NO_OBJ ),
|
||||
T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
|
||||
T01("signing-ed25519", K_SIGNING_CERT_ED, NO_ARGS , NEED_OBJ ),
|
||||
T01("shared-rand-participate",K_SR_FLAG, NO_ARGS, NO_OBJ ),
|
||||
T0N("shared-rand-commit", K_COMMIT, GE(3), NO_OBJ ),
|
||||
T01("shared-rand-previous-value", K_PREVIOUS_SRV,EQ(2), NO_OBJ ),
|
||||
T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
|
||||
T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ),
|
||||
|
||||
CERTIFICATE_MEMBERS
|
||||
@ -486,6 +497,9 @@ static token_rule_t networkstatus_consensus_token_table[] = {
|
||||
T01("consensus-method", K_CONSENSUS_METHOD, EQ(1), NO_OBJ),
|
||||
T01("params", K_PARAMS, ARGS, NO_OBJ ),
|
||||
|
||||
T01("shared-rand-previous-value", K_PREVIOUS_SRV, EQ(2), NO_OBJ ),
|
||||
T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
|
||||
|
||||
END_OF_TABLE
|
||||
};
|
||||
|
||||
@ -3391,6 +3405,134 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
|
||||
return valid;
|
||||
}
|
||||
|
||||
/** Parse and extract all SR commits from <b>tokens</b> and place them in
|
||||
* <b>ns</b>. */
|
||||
static void
|
||||
extract_shared_random_commits(networkstatus_t *ns, smartlist_t *tokens)
|
||||
{
|
||||
smartlist_t *chunks = NULL;
|
||||
|
||||
tor_assert(ns);
|
||||
tor_assert(tokens);
|
||||
/* Commits are only present in a vote. */
|
||||
tor_assert(ns->type == NS_TYPE_VOTE);
|
||||
|
||||
ns->sr_info.commits = smartlist_new();
|
||||
|
||||
smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT);
|
||||
/* It's normal that a vote might contain no commits even if it participates
|
||||
* in the SR protocol. Don't treat it as an error. */
|
||||
if (commits == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Parse the commit. We do NO validation of number of arguments or ordering
|
||||
* for forward compatibility, it's the parse commit job to inform us if it's
|
||||
* supported or not. */
|
||||
chunks = smartlist_new();
|
||||
SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) {
|
||||
/* Extract all arguments and put them in the chunks list. */
|
||||
for (int i = 0; i < tok->n_args; i++) {
|
||||
smartlist_add(chunks, tok->args[i]);
|
||||
}
|
||||
sr_commit_t *commit = sr_parse_commit(chunks);
|
||||
smartlist_clear(chunks);
|
||||
if (commit == NULL) {
|
||||
/* Get voter identity so we can warn that this dirauth vote contains
|
||||
* commit we can't parse. */
|
||||
networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0);
|
||||
tor_assert(voter);
|
||||
log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.",
|
||||
escaped(tok->object_body),
|
||||
hex_str(voter->identity_digest,
|
||||
sizeof(voter->identity_digest)));
|
||||
/* Commitment couldn't be parsed. Continue onto the next commit because
|
||||
* this one could be unsupported for instance. */
|
||||
continue;
|
||||
}
|
||||
/* Add newly created commit object to the vote. */
|
||||
smartlist_add(ns->sr_info.commits, commit);
|
||||
} SMARTLIST_FOREACH_END(tok);
|
||||
|
||||
end:
|
||||
smartlist_free(chunks);
|
||||
smartlist_free(commits);
|
||||
}
|
||||
|
||||
/** Check if a shared random value of type <b>srv_type</b> is in
|
||||
* <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return
|
||||
* -1 on failure, 0 on success. The resulting srv is allocated on the heap and
|
||||
* it's the responsibility of the caller to free it. */
|
||||
static int
|
||||
extract_one_srv(smartlist_t *tokens, directory_keyword srv_type,
|
||||
sr_srv_t **srv_out)
|
||||
{
|
||||
int ret = -1;
|
||||
directory_token_t *tok;
|
||||
sr_srv_t *srv = NULL;
|
||||
smartlist_t *chunks;
|
||||
|
||||
tor_assert(tokens);
|
||||
|
||||
chunks = smartlist_new();
|
||||
tok = find_opt_by_keyword(tokens, srv_type);
|
||||
if (!tok) {
|
||||
/* That's fine, no SRV is allowed. */
|
||||
ret = 0;
|
||||
goto end;
|
||||
}
|
||||
for (int i = 0; i < tok->n_args; i++) {
|
||||
smartlist_add(chunks, tok->args[i]);
|
||||
}
|
||||
srv = sr_parse_srv(chunks);
|
||||
if (srv == NULL) {
|
||||
log_warn(LD_DIR, "SR: Unparseable SRV %s", escaped(tok->object_body));
|
||||
goto end;
|
||||
}
|
||||
/* All is good. */
|
||||
*srv_out = srv;
|
||||
ret = 0;
|
||||
end:
|
||||
smartlist_free(chunks);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Extract any shared random values found in <b>tokens</b> and place them in
|
||||
* the networkstatus <b>ns</b>. */
|
||||
static void
|
||||
extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
|
||||
{
|
||||
const char *voter_identity;
|
||||
networkstatus_voter_info_t *voter;
|
||||
|
||||
tor_assert(ns);
|
||||
tor_assert(tokens);
|
||||
/* Can be only one of them else code flow. */
|
||||
tor_assert(ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS);
|
||||
|
||||
if (ns->type == NS_TYPE_VOTE) {
|
||||
voter = smartlist_get(ns->voters, 0);
|
||||
tor_assert(voter);
|
||||
voter_identity = hex_str(voter->identity_digest,
|
||||
sizeof(voter->identity_digest));
|
||||
} else {
|
||||
/* Consensus has multiple voters so no specific voter. */
|
||||
voter_identity = "consensus";
|
||||
}
|
||||
|
||||
/* We extract both and on error, everything is stopped because it means
|
||||
* the votes is malformed for the shared random value(s). */
|
||||
if (extract_one_srv(tokens, K_PREVIOUS_SRV, &ns->sr_info.previous_srv) < 0) {
|
||||
log_warn(LD_DIR, "SR: Unable to parse previous SRV from %s",
|
||||
voter_identity);
|
||||
/* Maybe we have a chance with the current SRV so let's try it anyway. */
|
||||
}
|
||||
if (extract_one_srv(tokens, K_CURRENT_SRV, &ns->sr_info.current_srv) < 0) {
|
||||
log_warn(LD_DIR, "SR: Unable to parse current SRV from %s",
|
||||
voter_identity);
|
||||
}
|
||||
}
|
||||
|
||||
/** Parse a v3 networkstatus vote, opinion, or consensus (depending on
|
||||
* ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
|
||||
networkstatus_t *
|
||||
@ -3735,6 +3877,22 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
|
||||
}
|
||||
}
|
||||
|
||||
/* If this is a vote document, check if information about the shared
|
||||
randomness protocol is included, and extract it. */
|
||||
if (ns->type == NS_TYPE_VOTE) {
|
||||
/* Does this authority participates in the SR protocol? */
|
||||
tok = find_opt_by_keyword(tokens, K_SR_FLAG);
|
||||
if (tok) {
|
||||
ns->sr_info.participate = 1;
|
||||
/* Get the SR commitments and reveals from the vote. */
|
||||
extract_shared_random_commits(ns, tokens);
|
||||
}
|
||||
}
|
||||
/* For both a vote and consensus, extract the shared random values. */
|
||||
if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
|
||||
extract_shared_random_srvs(ns, tokens);
|
||||
}
|
||||
|
||||
/* Parse routerstatus lines. */
|
||||
rs_tokens = smartlist_new();
|
||||
rs_area = memarea_new();
|
||||
|
1354
src/or/shared_random.c
Normal file
1354
src/or/shared_random.c
Normal file
File diff suppressed because it is too large
Load Diff
166
src/or/shared_random.h
Normal file
166
src/or/shared_random.h
Normal file
@ -0,0 +1,166 @@
|
||||
/* Copyright (c) 2016, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TOR_SHARED_RANDOM_H
|
||||
#define TOR_SHARED_RANDOM_H
|
||||
|
||||
/*
|
||||
* This file contains ABI/API of the shared random protocol defined in
|
||||
* proposal #250. Every public functions and data structure are namespaced
|
||||
* with "sr_" which stands for shared random.
|
||||
*/
|
||||
|
||||
#include "or.h"
|
||||
|
||||
/* Protocol version */
|
||||
#define SR_PROTO_VERSION 1
|
||||
/* Default digest algorithm. */
|
||||
#define SR_DIGEST_ALG DIGEST_SHA3_256
|
||||
/* Invariant token in the SRV calculation. */
|
||||
#define SR_SRV_TOKEN "shared-random"
|
||||
/* Don't count the NUL terminated byte even though the TOKEN has it. */
|
||||
#define SR_SRV_TOKEN_LEN (sizeof(SR_SRV_TOKEN) - 1)
|
||||
|
||||
/* Length of the random number (in bytes). */
|
||||
#define SR_RANDOM_NUMBER_LEN 32
|
||||
/* Size of a decoded commit value in a vote or state. It's a hash and a
|
||||
* timestamp. It adds up to 40 bytes. */
|
||||
#define SR_COMMIT_LEN (sizeof(uint64_t) + DIGEST256_LEN)
|
||||
/* Size of a decoded reveal value from a vote or state. It's a 64 bit
|
||||
* timestamp and the hashed random number. This adds up to 40 bytes. */
|
||||
#define SR_REVEAL_LEN (sizeof(uint64_t) + DIGEST256_LEN)
|
||||
/* Size of SRV message length. The construction is has follow:
|
||||
* "shared-random" | INT_8(reveal_num) | INT_4(version) | PREV_SRV */
|
||||
#define SR_SRV_MSG_LEN \
|
||||
(SR_SRV_TOKEN_LEN + sizeof(uint64_t) + sizeof(uint32_t) + DIGEST256_LEN)
|
||||
|
||||
/* Length of base64 encoded commit NOT including the NULL terminated byte.
|
||||
* Formula is taken from base64_encode_size. */
|
||||
#define SR_COMMIT_BASE64_LEN \
|
||||
(((SR_COMMIT_LEN - 1) / 3) * 4 + 4)
|
||||
/* Length of base64 encoded reveal NOT including the NULL terminated byte.
|
||||
* Formula is taken from base64_encode_size. This adds up to 56 bytes. */
|
||||
#define SR_REVEAL_BASE64_LEN \
|
||||
(((SR_REVEAL_LEN - 1) / 3) * 4 + 4)
|
||||
/* Length of base64 encoded shared random value. It's 32 bytes long so 44
|
||||
* bytes from the base64_encode_size formula. That includes the '='
|
||||
* character at the end. */
|
||||
#define SR_SRV_VALUE_BASE64_LEN \
|
||||
(((DIGEST256_LEN - 1) / 3) * 4 + 4)
|
||||
|
||||
/* Assert if commit valid flag is not set. */
|
||||
#define ASSERT_COMMIT_VALID(c) tor_assert((c)->valid)
|
||||
|
||||
/* Protocol phase. */
|
||||
typedef enum {
|
||||
/* Commitment phase */
|
||||
SR_PHASE_COMMIT = 1,
|
||||
/* Reveal phase */
|
||||
SR_PHASE_REVEAL = 2,
|
||||
} sr_phase_t;
|
||||
|
||||
/* A shared random value (SRV). */
|
||||
typedef struct sr_srv_t {
|
||||
/* The number of reveal values used to derive this SRV. */
|
||||
uint64_t num_reveals;
|
||||
/* The actual value. This is the stored result of SHA3-256. */
|
||||
uint8_t value[DIGEST256_LEN];
|
||||
} sr_srv_t;
|
||||
|
||||
/* A commit (either ours or from another authority). */
|
||||
typedef struct sr_commit_t {
|
||||
/* Hashing algorithm used. */
|
||||
digest_algorithm_t alg;
|
||||
/* Indicate if this commit has been verified thus valid. */
|
||||
unsigned int valid:1;
|
||||
|
||||
/* Commit owner info */
|
||||
|
||||
/* The RSA identity key of the authority. */
|
||||
char rsa_identity[DIGEST_LEN];
|
||||
|
||||
/* Commitment information */
|
||||
|
||||
/* Timestamp of reveal. Correspond to TIMESTAMP. */
|
||||
uint64_t reveal_ts;
|
||||
/* H(REVEAL) as found in COMMIT message. */
|
||||
char hashed_reveal[DIGEST256_LEN];
|
||||
/* Base64 encoded COMMIT. We use this to put it in our vote. */
|
||||
char encoded_commit[SR_COMMIT_BASE64_LEN + 1];
|
||||
|
||||
/* Reveal information */
|
||||
|
||||
/* H(RN) which is what we used as the random value for this commit. We
|
||||
* don't use the raw bytes since those are sent on the network thus
|
||||
* avoiding possible information leaks of our PRNG. */
|
||||
uint8_t random_number[SR_RANDOM_NUMBER_LEN];
|
||||
/* Timestamp of commit. Correspond to TIMESTAMP. */
|
||||
uint64_t commit_ts;
|
||||
/* This is the whole reveal message. We use it during verification */
|
||||
char encoded_reveal[SR_REVEAL_BASE64_LEN + 1];
|
||||
} sr_commit_t;
|
||||
|
||||
/* API */
|
||||
|
||||
/* Public methods: */
|
||||
|
||||
int sr_init(int save_to_disk);
|
||||
void sr_save_and_cleanup(void);
|
||||
void sr_act_post_consensus(const networkstatus_t *consensus);
|
||||
void sr_handle_received_commits(smartlist_t *commits,
|
||||
crypto_pk_t *voter_key);
|
||||
sr_commit_t *sr_parse_commit(const smartlist_t *args);
|
||||
sr_srv_t *sr_parse_srv(const smartlist_t *args);
|
||||
char *sr_get_string_for_vote(void);
|
||||
char *sr_get_string_for_consensus(const smartlist_t *votes,
|
||||
int32_t num_srv_agreements);
|
||||
void sr_commit_free(sr_commit_t *commit);
|
||||
void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv);
|
||||
|
||||
/* Private methods (only used by shared_random_state.c): */
|
||||
static inline
|
||||
const char *sr_commit_get_rsa_fpr(const sr_commit_t *commit)
|
||||
{
|
||||
return hex_str((const char *) commit->rsa_identity,
|
||||
sizeof(commit->rsa_identity));
|
||||
}
|
||||
|
||||
void sr_compute_srv(void);
|
||||
sr_commit_t *sr_generate_our_commit(time_t timestamp,
|
||||
const authority_cert_t *my_rsa_cert);
|
||||
#ifdef SHARED_RANDOM_PRIVATE
|
||||
|
||||
/* Encode */
|
||||
STATIC int reveal_encode(const sr_commit_t *commit, char *dst, size_t len);
|
||||
STATIC int commit_encode(const sr_commit_t *commit, char *dst, size_t len);
|
||||
/* Decode. */
|
||||
STATIC int commit_decode(const char *encoded, sr_commit_t *commit);
|
||||
STATIC int reveal_decode(const char *encoded, sr_commit_t *commit);
|
||||
|
||||
STATIC int commit_has_reveal_value(const sr_commit_t *commit);
|
||||
|
||||
STATIC int verify_commit_and_reveal(const sr_commit_t *commit);
|
||||
|
||||
STATIC sr_srv_t *get_majority_srv_from_votes(const smartlist_t *votes,
|
||||
int current);
|
||||
|
||||
STATIC void save_commit_to_state(sr_commit_t *commit);
|
||||
STATIC sr_srv_t *srv_dup(const sr_srv_t *orig);
|
||||
STATIC int commitments_are_the_same(const sr_commit_t *commit_one,
|
||||
const sr_commit_t *commit_two);
|
||||
STATIC int commit_is_authoritative(const sr_commit_t *commit,
|
||||
const char *voter_key);
|
||||
STATIC int should_keep_commit(const sr_commit_t *commit,
|
||||
const char *voter_key,
|
||||
sr_phase_t phase);
|
||||
STATIC void save_commit_during_reveal_phase(const sr_commit_t *commit);
|
||||
|
||||
#endif /* SHARED_RANDOM_PRIVATE */
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
|
||||
void set_num_srv_agreements(int32_t value);
|
||||
|
||||
#endif /* TOR_UNIT_TESTS */
|
||||
|
||||
#endif /* TOR_SHARED_RANDOM_H */
|
1353
src/or/shared_random_state.c
Normal file
1353
src/or/shared_random_state.c
Normal file
File diff suppressed because it is too large
Load Diff
146
src/or/shared_random_state.h
Normal file
146
src/or/shared_random_state.h
Normal file
@ -0,0 +1,146 @@
|
||||
/* Copyright (c) 2016, The Tor Project, Inc. */
|
||||
/* See LICENSE for licensing information */
|
||||
|
||||
#ifndef TOR_SHARED_RANDOM_STATE_H
|
||||
#define TOR_SHARED_RANDOM_STATE_H
|
||||
|
||||
#include "shared_random.h"
|
||||
|
||||
/* Action that can be performed on the state for any objects. */
|
||||
typedef enum {
|
||||
SR_STATE_ACTION_GET = 1,
|
||||
SR_STATE_ACTION_PUT = 2,
|
||||
SR_STATE_ACTION_DEL = 3,
|
||||
SR_STATE_ACTION_DEL_ALL = 4,
|
||||
SR_STATE_ACTION_SAVE = 5,
|
||||
} sr_state_action_t;
|
||||
|
||||
/* Object in the state that can be queried through the state API. */
|
||||
typedef enum {
|
||||
/* Will return a single commit using an authority identity key. */
|
||||
SR_STATE_OBJ_COMMIT,
|
||||
/* Returns the entire list of commits from the state. */
|
||||
SR_STATE_OBJ_COMMITS,
|
||||
/* Return the current SRV object pointer. */
|
||||
SR_STATE_OBJ_CURSRV,
|
||||
/* Return the previous SRV object pointer. */
|
||||
SR_STATE_OBJ_PREVSRV,
|
||||
/* Return the phase. */
|
||||
SR_STATE_OBJ_PHASE,
|
||||
/* Get or Put the valid after time. */
|
||||
SR_STATE_OBJ_VALID_AFTER,
|
||||
} sr_state_object_t;
|
||||
|
||||
/* State of the protocol. It's also saved on disk in fname. This data
|
||||
* structure MUST be synchronized at all time with the one on disk. */
|
||||
typedef struct sr_state_t {
|
||||
/* Filename of the state file on disk. */
|
||||
char *fname;
|
||||
/* Version of the protocol. */
|
||||
uint32_t version;
|
||||
/* The valid-after of the voting period we have prepared the state for. */
|
||||
time_t valid_after;
|
||||
/* Until when is this state valid? */
|
||||
time_t valid_until;
|
||||
/* Protocol phase. */
|
||||
sr_phase_t phase;
|
||||
|
||||
/* Number of runs completed. */
|
||||
uint64_t n_protocol_runs;
|
||||
/* The number of commitment rounds we've performed in this protocol run. */
|
||||
unsigned int n_commit_rounds;
|
||||
/* The number of reveal rounds we've performed in this protocol run. */
|
||||
unsigned int n_reveal_rounds;
|
||||
|
||||
/* A map of all the received commitments for this protocol run. This is
|
||||
* indexed by authority RSA identity digest. */
|
||||
digestmap_t *commits;
|
||||
|
||||
/* Current and previous shared random value. */
|
||||
sr_srv_t *previous_srv;
|
||||
sr_srv_t *current_srv;
|
||||
|
||||
/* Indicate if the state contains an SRV that was _just_ generated. This is
|
||||
* used during voting so that we know whether to use the super majority rule
|
||||
* or not when deciding on keeping it for the consensus. It is _always_ set
|
||||
* to 0 post consensus.
|
||||
*
|
||||
* EDGE CASE: if an authority computes a new SRV then immediately reboots
|
||||
* and, once back up, votes for the current round, it won't know if the
|
||||
* SRV is fresh or not ultimately making it _NOT_ use the super majority
|
||||
* when deciding to put or not the SRV in the consensus. This is for now
|
||||
* an acceptable very rare edge case. */
|
||||
unsigned int is_srv_fresh:1;
|
||||
} sr_state_t;
|
||||
|
||||
/* Persistent state of the protocol, as saved to disk. */
|
||||
typedef struct sr_disk_state_t {
|
||||
uint32_t magic_;
|
||||
/* Version of the protocol. */
|
||||
uint32_t Version;
|
||||
/* Version of our running tor. */
|
||||
char *TorVersion;
|
||||
/* Creation time of this state */
|
||||
time_t ValidAfter;
|
||||
/* State valid until? */
|
||||
time_t ValidUntil;
|
||||
/* All commits seen that are valid. */
|
||||
config_line_t *Commit;
|
||||
/* Previous and current shared random value. */
|
||||
config_line_t *SharedRandValues;
|
||||
/* Extra Lines for configuration we might not know. */
|
||||
config_line_t *ExtraLines;
|
||||
} sr_disk_state_t;
|
||||
|
||||
/* API */
|
||||
|
||||
/* Public methods: */
|
||||
|
||||
void sr_state_update(time_t valid_after);
|
||||
|
||||
/* Private methods (only used by shared-random.c): */
|
||||
|
||||
void sr_state_set_valid_after(time_t valid_after);
|
||||
sr_phase_t sr_state_get_phase(void);
|
||||
const sr_srv_t *sr_state_get_previous_srv(void);
|
||||
const sr_srv_t *sr_state_get_current_srv(void);
|
||||
void sr_state_set_previous_srv(const sr_srv_t *srv);
|
||||
void sr_state_set_current_srv(const sr_srv_t *srv);
|
||||
void sr_state_clean_srvs(void);
|
||||
digestmap_t *sr_state_get_commits(void);
|
||||
sr_commit_t *sr_state_get_commit(const char *rsa_fpr);
|
||||
void sr_state_add_commit(sr_commit_t *commit);
|
||||
void sr_state_delete_commits(void);
|
||||
void sr_state_copy_reveal_info(sr_commit_t *saved_commit,
|
||||
const sr_commit_t *commit);
|
||||
unsigned int sr_state_srv_is_fresh(void);
|
||||
void sr_state_set_fresh_srv(void);
|
||||
void sr_state_unset_fresh_srv(void);
|
||||
int sr_state_init(int save_to_disk, int read_from_disk);
|
||||
int sr_state_is_initialized(void);
|
||||
void sr_state_save(void);
|
||||
void sr_state_free(void);
|
||||
|
||||
#ifdef SHARED_RANDOM_STATE_PRIVATE
|
||||
|
||||
STATIC int disk_state_load_from_disk_impl(const char *fname);
|
||||
|
||||
STATIC sr_phase_t get_sr_protocol_phase(time_t valid_after);
|
||||
|
||||
STATIC time_t get_state_valid_until_time(time_t now);
|
||||
STATIC const char *get_phase_str(sr_phase_t phase);
|
||||
STATIC void reset_state_for_new_protocol_run(time_t valid_after);
|
||||
STATIC void new_protocol_run(time_t valid_after);
|
||||
STATIC void state_rotate_srv(void);
|
||||
STATIC int is_phase_transition(sr_phase_t next_phase);
|
||||
|
||||
#endif /* SHARED_RANDOM_STATE_PRIVATE */
|
||||
|
||||
#ifdef TOR_UNIT_TESTS
|
||||
|
||||
STATIC void set_sr_phase(sr_phase_t phase);
|
||||
STATIC sr_state_t *get_sr_state(void);
|
||||
|
||||
#endif /* TOR_UNIT_TESTS */
|
||||
|
||||
#endif /* TOR_SHARED_RANDOM_STATE_H */
|
@ -116,6 +116,7 @@ src_test_test_SOURCES = \
|
||||
src/test/test_routerlist.c \
|
||||
src/test/test_routerset.c \
|
||||
src/test/test_scheduler.c \
|
||||
src/test/test_shared_random.c \
|
||||
src/test/test_socks.c \
|
||||
src/test/test_status.c \
|
||||
src/test/test_threads.c \
|
||||
|
71
src/test/sr_srv_calc_ref.py
Normal file
71
src/test/sr_srv_calc_ref.py
Normal file
@ -0,0 +1,71 @@
|
||||
# This is a reference implementation of the SRV calculation for prop250. We
|
||||
# use it to generate a test vector for the test_sr_compute_srv() unittest.
|
||||
# (./test shared-random/sr_compute_srv)
|
||||
#
|
||||
# Here is the SRV computation formula:
|
||||
#
|
||||
# HASHED_REVEALS = H(ID_a | R_a | ID_b | R_b | ..)
|
||||
#
|
||||
# SRV = SHA3-256("shared-random" | INT_8(reveal_num) | INT_4(version) |
|
||||
# HASHED_REVEALS | previous_SRV)
|
||||
#
|
||||
|
||||
import sys
|
||||
import hashlib
|
||||
import struct
|
||||
|
||||
# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires
|
||||
# the pysha3 package (pip install pysha3).
|
||||
if sys.version_info < (3, 6):
|
||||
import sha3
|
||||
|
||||
# Test vector to make sure the right sha3 version will be used. pysha3 < 1.0
|
||||
# used the old Keccak implementation. During the finalization of SHA3, NIST
|
||||
# changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function
|
||||
# stayed the same. pysha3 1.0 provides the previous Keccak hash, too.
|
||||
TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51"
|
||||
if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest():
|
||||
print("pysha3 version is < 1.0. Please install from:")
|
||||
print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3")
|
||||
sys.exit(1)
|
||||
|
||||
# In this example, we use three reveal values.
|
||||
reveal_num = 3
|
||||
version = 1
|
||||
|
||||
# We set directly the ascii value because memset(buf, 'A', 20) makes it to 20
|
||||
# times "41" in the final string.
|
||||
|
||||
# Identity and reveal value of dirauth a
|
||||
ID_a = 20 * "41" # RSA identity of 40 base16 bytes.
|
||||
R_a = 56 * 'A' # 56 base64 characters
|
||||
|
||||
# Identity and reveal value of dirauth b
|
||||
ID_b = 20 * "42" # RSA identity of 40 base16 bytes.
|
||||
R_b = 56 * 'B' # 56 base64 characters
|
||||
|
||||
# Identity and reveal value of dirauth c
|
||||
ID_c = 20 * "43" # RSA identity of 40 base16 bytes.
|
||||
R_c = 56 * 'C' # 56 base64 characters
|
||||
|
||||
# Concatenate them all together and hash them to form HASHED_REVEALS.
|
||||
REVEALS = (ID_a + R_a + ID_b + R_b + ID_c + R_c).encode()
|
||||
hashed_reveals_object = hashlib.sha3_256(REVEALS)
|
||||
hashed_reveals = hashed_reveals_object.digest()
|
||||
|
||||
previous_SRV = (32 * 'Z').encode()
|
||||
|
||||
# Now form the message.
|
||||
#srv_msg = struct.pack('13sQL256ss', "shared-random", reveal_num, version,
|
||||
# hashed_reveals, previous_SRV)
|
||||
invariant_token = b"shared-random"
|
||||
srv_msg = invariant_token + \
|
||||
struct.pack('!QL', reveal_num, version) + \
|
||||
hashed_reveals + \
|
||||
previous_SRV
|
||||
|
||||
# Now calculate the HMAC
|
||||
srv = hashlib.sha3_256(srv_msg)
|
||||
print("%s" % srv.hexdigest().upper())
|
||||
|
||||
# 2A9B1D6237DAB312A40F575DA85C147663E7ED3F80E9555395F15B515C74253D
|
@ -1170,6 +1170,7 @@ struct testgroup_t testgroups[] = {
|
||||
{ "routerset/" , routerset_tests },
|
||||
{ "scheduler/", scheduler_tests },
|
||||
{ "socks/", socks_tests },
|
||||
{ "shared-random/", sr_tests },
|
||||
{ "status/" , status_tests },
|
||||
{ "tortls/", tortls_tests },
|
||||
{ "util/", util_tests },
|
||||
|
@ -225,6 +225,7 @@ extern struct testcase_t util_format_tests[];
|
||||
extern struct testcase_t util_process_tests[];
|
||||
extern struct testcase_t dns_tests[];
|
||||
extern struct testcase_t handle_tests[];
|
||||
extern struct testcase_t sr_tests[];
|
||||
|
||||
extern struct testcase_t slow_crypto_tests[];
|
||||
extern struct testcase_t slow_util_tests[];
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include "routerlist.h"
|
||||
#include "routerparse.h"
|
||||
#include "routerset.h"
|
||||
#include "shared_random_state.h"
|
||||
#include "test.h"
|
||||
#include "test_dir_common.h"
|
||||
#include "torcert.h"
|
||||
@ -1436,6 +1437,19 @@ test_dir_measured_bw_kb_cache(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
static char *
|
||||
my_dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
|
||||
{
|
||||
smartlist_t *s = dirvote_compute_params(votes, method, total_authorities);
|
||||
tor_assert(s);
|
||||
char *res = smartlist_join_strings(s, " ", 0, NULL);
|
||||
SMARTLIST_FOREACH(s, char *, cp, tor_free(cp));
|
||||
smartlist_free(s);
|
||||
return res;
|
||||
}
|
||||
|
||||
#define dirvote_compute_params my_dirvote_compute_params
|
||||
|
||||
static void
|
||||
test_dir_param_voting(void *arg)
|
||||
{
|
||||
@ -1545,6 +1559,43 @@ test_dir_param_voting(void *arg)
|
||||
return;
|
||||
}
|
||||
|
||||
static void
|
||||
test_dir_param_voting_lookup(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
smartlist_t *lst = smartlist_new();
|
||||
|
||||
smartlist_split_string(lst,
|
||||
"moomin=9 moomin=10 moomintroll=5 fred "
|
||||
"jack= electricity=sdk opa=6z abc=9 abcd=99",
|
||||
NULL, 0, 0);
|
||||
|
||||
tt_int_op(1000,
|
||||
OP_EQ, dirvote_get_intermediate_param_value(lst, "ab", 1000));
|
||||
tt_int_op(9, OP_EQ, dirvote_get_intermediate_param_value(lst, "abc", 1000));
|
||||
tt_int_op(99, OP_EQ, dirvote_get_intermediate_param_value(lst, "abcd", 1000));
|
||||
|
||||
/* moomin appears twice. */
|
||||
tt_int_op(-100, OP_EQ,
|
||||
dirvote_get_intermediate_param_value(lst, "moomin", -100));
|
||||
/* fred and jack are truncated */
|
||||
tt_int_op(-100, OP_EQ,
|
||||
dirvote_get_intermediate_param_value(lst, "fred", -100));
|
||||
tt_int_op(-100, OP_EQ,
|
||||
dirvote_get_intermediate_param_value(lst, "jack", -100));
|
||||
/* electricity and opa aren't integers. */
|
||||
tt_int_op(-100, OP_EQ,
|
||||
dirvote_get_intermediate_param_value(lst, "electricity", -100));
|
||||
tt_int_op(-100, OP_EQ,
|
||||
dirvote_get_intermediate_param_value(lst, "opa", -100));
|
||||
|
||||
done:
|
||||
SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp));
|
||||
smartlist_free(lst);
|
||||
}
|
||||
|
||||
#undef dirvote_compute_params
|
||||
|
||||
/** Helper: Test that two networkstatus_voter_info_t do in fact represent the
|
||||
* same voting authority, and that they do in fact have all the same
|
||||
* information. */
|
||||
@ -1789,6 +1840,15 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
|
||||
return;
|
||||
}
|
||||
|
||||
static authority_cert_t *mock_cert;
|
||||
|
||||
static authority_cert_t *
|
||||
get_my_v3_authority_cert_m(void)
|
||||
{
|
||||
tor_assert(mock_cert);
|
||||
return mock_cert;
|
||||
}
|
||||
|
||||
/** Run a unit tests for generating and parsing networkstatuses, with
|
||||
* the supply test fns. */
|
||||
static void
|
||||
@ -1832,10 +1892,30 @@ test_a_networkstatus(
|
||||
tt_assert(rs_test);
|
||||
tt_assert(vrs_test);
|
||||
|
||||
tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3,
|
||||
&sign_skey_1, &sign_skey_2,
|
||||
&sign_skey_3));
|
||||
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
|
||||
|
||||
/* Parse certificates and keys. */
|
||||
cert1 = mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
|
||||
tt_assert(cert1);
|
||||
cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
|
||||
tt_assert(cert2);
|
||||
cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
|
||||
tt_assert(cert3);
|
||||
sign_skey_1 = crypto_pk_new();
|
||||
sign_skey_2 = crypto_pk_new();
|
||||
sign_skey_3 = crypto_pk_new();
|
||||
sign_skey_leg1 = pk_generate(4);
|
||||
sr_state_init(0, 0);
|
||||
|
||||
tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1,
|
||||
AUTHORITY_SIGNKEY_1, -1));
|
||||
tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_2,
|
||||
AUTHORITY_SIGNKEY_2, -1));
|
||||
tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_3,
|
||||
AUTHORITY_SIGNKEY_3, -1));
|
||||
|
||||
tt_assert(!crypto_pk_cmp_keys(sign_skey_1, cert1->signing_key));
|
||||
tt_assert(!crypto_pk_cmp_keys(sign_skey_2, cert2->signing_key));
|
||||
|
||||
tt_assert(!dir_common_construct_vote_1(&vote, cert1, sign_skey_1, vrs_gen,
|
||||
&v1, &n_vrs, now, 1));
|
||||
@ -5287,6 +5367,7 @@ struct testcase_t dir_tests[] = {
|
||||
DIR_LEGACY(measured_bw_kb),
|
||||
DIR_LEGACY(measured_bw_kb_cache),
|
||||
DIR_LEGACY(param_voting),
|
||||
DIR(param_voting_lookup, 0),
|
||||
DIR_LEGACY(v3_networkstatus),
|
||||
DIR(random_weighted, 0),
|
||||
DIR(scale_bw, 0),
|
||||
|
@ -19,13 +19,24 @@
|
||||
#include "networkstatus.h"
|
||||
#include "nodelist.h"
|
||||
#include "policies.h"
|
||||
#include "router.h"
|
||||
#include "routerlist.h"
|
||||
#include "routerparse.h"
|
||||
#include "shared_random.h"
|
||||
#include "test.h"
|
||||
#include "test_dir_common.h"
|
||||
|
||||
void construct_consensus(char **consensus_text_md);
|
||||
|
||||
static authority_cert_t *mock_cert;
|
||||
|
||||
static authority_cert_t *
|
||||
get_my_v3_authority_cert_m(void)
|
||||
{
|
||||
tor_assert(mock_cert);
|
||||
return mock_cert;
|
||||
}
|
||||
|
||||
/* 4 digests + 3 sep + pre + post + NULL */
|
||||
static char output[4*BASE64_DIGEST256_LEN+3+2+2+1];
|
||||
|
||||
@ -227,6 +238,12 @@ test_router_pick_directory_server_impl(void *arg)
|
||||
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
|
||||
tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));
|
||||
|
||||
/* Init SR subsystem. */
|
||||
MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
|
||||
mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
|
||||
sr_init(0);
|
||||
UNMOCK(get_my_v3_authority_cert);
|
||||
|
||||
/* No consensus available, fail early */
|
||||
rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
|
||||
tt_assert(rs == NULL);
|
||||
|
1261
src/test/test_shared_random.c
Normal file
1261
src/test/test_shared_random.c
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user