Merge remote-tracking branch 'dgoulet/ticket16943_029_05-squashed'

Trivial Conflicts:
	src/or/or.h
	src/or/routerparse.c
This commit is contained in:
Nick Mathewson 2016-07-01 15:29:05 -04:00
commit aaa3129043
27 changed files with 4888 additions and 68 deletions

8
changes/bug16943 Normal file
View 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.

View File

@ -2239,6 +2239,12 @@ on the public Tor network.
in a journal if it is new, or if it differs from the most recently 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) 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__:: [[BridgePassword]] **BridgePassword** __Password__::
If set, contains an HTTP authenticator that tells a bridge authority to If set, contains an HTTP authenticator that tells a bridge authority to
serve all requested bridge information. Used by the (only partially serve all requested bridge information. Used by the (only partially

View File

@ -5687,3 +5687,24 @@ clamp_double_to_int64(double number)
return signbit(number) ? INT64_MIN : INT64_MAX; 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);
}

View File

@ -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) void *tor_memdup_nulterm_(const void *mem, size_t len DMALLOC_PARAMS)
ATTR_MALLOC ATTR_NONNULL((1)); ATTR_MALLOC ATTR_NONNULL((1));
void tor_free_(void *mem); void tor_free_(void *mem);
uint64_t tor_htonll(uint64_t a);
uint64_t tor_ntohll(uint64_t a);
#ifdef USE_DMALLOC #ifdef USE_DMALLOC
extern int dmalloc_free(const char *file, const int line, void *pnt, extern int dmalloc_free(const char *file, const int line, void *pnt,
const int func_id); const int func_id);

View File

@ -440,6 +440,7 @@ static config_var_t option_vars_[] = {
V(UseNTorHandshake, AUTOBOOL, "1"), V(UseNTorHandshake, AUTOBOOL, "1"),
V(User, STRING, NULL), V(User, STRING, NULL),
V(UserspaceIOCPBuffers, BOOL, "0"), V(UserspaceIOCPBuffers, BOOL, "0"),
V(AuthDirSharedRandomness, BOOL, "1"),
OBSOLETE("V1AuthoritativeDirectory"), OBSOLETE("V1AuthoritativeDirectory"),
OBSOLETE("V2AuthoritativeDirectory"), OBSOLETE("V2AuthoritativeDirectory"),
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"), VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),

View File

@ -30,6 +30,7 @@
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "routerset.h" #include "routerset.h"
#include "shared_random.h"
#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO) #if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
#ifndef OPENBSD #ifndef OPENBSD
@ -2026,6 +2027,10 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
update_microdescs_from_networkstatus(now); update_microdescs_from_networkstatus(now);
update_microdesc_downloads(now); update_microdesc_downloads(now);
directory_info_has_arrived(now, 0, 0); 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."); log_info(LD_DIR, "Successfully loaded consensus.");
} }

View File

@ -15,10 +15,12 @@
#include "policies.h" #include "policies.h"
#include "rephist.h" #include "rephist.h"
#include "router.h" #include "router.h"
#include "routerkeys.h"
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "entrynodes.h" /* needed for guardfraction methods */ #include "entrynodes.h" /* needed for guardfraction methods */
#include "torcert.h" #include "torcert.h"
#include "shared_random_state.h"
/** /**
* \file dirvote.c * \file dirvote.c
@ -73,6 +75,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
char digest[DIGEST_LEN]; char digest[DIGEST_LEN];
uint32_t addr; uint32_t addr;
char *client_versions_line = NULL, *server_versions_line = NULL; char *client_versions_line = NULL, *server_versions_line = NULL;
char *shared_random_vote_str = NULL;
networkstatus_voter_info_t *voter; networkstatus_voter_info_t *voter;
char *status = NULL; char *status = NULL;
@ -114,6 +117,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
packages = tor_strdup(""); 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 published[ISO_TIME_LEN+1];
char va[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" "flag-thresholds %s\n"
"params %s\n" "params %s\n"
"dir-source %s %s %s %s %d %d\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", v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion",
methods, methods,
published, va, fu, vu, published, va, fu, vu,
@ -166,12 +173,15 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
params, params,
voter->nickname, fingerprint, voter->address, voter->nickname, fingerprint, voter->address,
fmt_addr32(addr), voter->dir_port, voter->or_port, 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(params);
tor_free(flags); tor_free(flags);
tor_free(flag_thresholds); tor_free(flag_thresholds);
tor_free(methods); tor_free(methods);
tor_free(shared_random_vote_str);
if (!tor_digest_is_zero(voter->legacy_id_digest)) { if (!tor_digest_is_zero(voter->legacy_id_digest)) {
char fpbuf[HEX_DIGEST_LEN+1]; char fpbuf[HEX_DIGEST_LEN+1];
@ -608,15 +618,47 @@ compute_consensus_versions_list(smartlist_t *lst, int n_versioning)
return result; 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 /** Minimum number of directory authorities voting for a parameter to
* include it in the consensus, if consensus method 12 or later is to be * include it in the consensus, if consensus method 12 or later is to be
* used. See proposal 178 for details. */ * used. See proposal 178 for details. */
#define MIN_VOTES_FOR_PARAM 3 #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. * 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) dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
{ {
int i; int i;
@ -625,7 +667,6 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
int cur_param_len; int cur_param_len;
const char *cur_param; const char *cur_param;
const char *eq; const char *eq;
char *result;
const int n_votes = smartlist_len(votes); const int n_votes = smartlist_len(votes);
smartlist_t *output; smartlist_t *output;
@ -647,8 +688,7 @@ dirvote_compute_params(smartlist_t *votes, int method, int total_authorities)
if (smartlist_len(param_list) == 0) { if (smartlist_len(param_list) == 0) {
tor_free(vals); tor_free(vals);
smartlist_free(param_list); return param_list;
return NULL;
} }
smartlist_sort_strings(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); } 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); smartlist_free(param_list);
tor_free(vals); tor_free(vals);
return result; return output;
} }
#define RANGE_CHECK(a,b,c,d,e,f,g,mx) \ #define RANGE_CHECK(a,b,c,d,e,f,g,mx) \
@ -1148,6 +1185,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
char *packages = NULL; char *packages = NULL;
int added_weights = 0; int added_weights = 0;
dircollator_t *collator = NULL; dircollator_t *collator = NULL;
smartlist_t *param_list = NULL;
tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC); tor_assert(flavor == FLAV_NS || flavor == FLAV_MICRODESC);
tor_assert(total_authorities >= smartlist_len(votes)); tor_assert(total_authorities >= smartlist_len(votes));
tor_assert(total_authorities > 0); tor_assert(total_authorities > 0);
@ -1292,14 +1331,31 @@ networkstatus_compute_consensus(smartlist_t *votes,
tor_free(flaglist); tor_free(flaglist);
} }
params = dirvote_compute_params(votes, consensus_method, param_list = dirvote_compute_params(votes, consensus_method,
total_authorities); 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, tor_strdup("params "));
smartlist_add(chunks, params); smartlist_add(chunks, params);
smartlist_add(chunks, tor_strdup("\n")); 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. */ /* Sort the votes. */
smartlist_sort(votes, compare_votes_by_authority_id_); smartlist_sort(votes, compare_votes_by_authority_id_);
/* Add the authority sections. */ /* Add the authority sections. */
@ -1351,7 +1407,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
if (consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW) { if (consensus_method >= MIN_METHOD_TO_CLIP_UNMEASURED_BW) {
char *max_unmeasured_param = NULL; 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 (params) {
if (strcmpstart(params, "maxunmeasuredbw=") == 0) if (strcmpstart(params, "maxunmeasuredbw=") == 0)
max_unmeasured_param = params; max_unmeasured_param = params;
@ -1906,7 +1962,7 @@ networkstatus_compute_consensus(smartlist_t *votes,
// Parse params, extract BW_WEIGHT_SCALE if present // Parse params, extract BW_WEIGHT_SCALE if present
// DO NOT use consensus_param_bw_weight_scale() in this code! // DO NOT use consensus_param_bw_weight_scale() in this code!
// The consensus is not formed yet! // 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 (params) {
if (strcmpstart(params, "bwweightscale=") == 0) if (strcmpstart(params, "bwweightscale=") == 0)
bw_weight_param = params; bw_weight_param = params;
@ -2026,6 +2082,8 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_free(flags); smartlist_free(flags);
SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp)); SMARTLIST_FOREACH(chunks, char *, cp, tor_free(cp));
smartlist_free(chunks); smartlist_free(chunks);
SMARTLIST_FOREACH(param_list, char *, cp, tor_free(cp));
smartlist_free(param_list);
return result; return result;
} }
@ -2511,50 +2569,60 @@ dirvote_get_start_of_next_interval(time_t now, int interval, int offset)
return next; return next;
} }
/** Scheduling information for a voting interval. */ /* Using the time <b>now</b>, return the next voting valid-after time. */
static struct { time_t
/** When do we generate and distribute our vote for this interval? */ get_next_valid_after_time(time_t now)
time_t voting_starts; {
/** When do we send an HTTP request for any votes that we haven't time_t next_valid_after_time;
* been posted yet?*/ const or_options_t *options = get_options();
time_t fetch_missing_votes; voting_schedule_t *new_voting_schedule =
/** When do we give up on getting more votes and generate a consensus? */ get_voting_schedule(options, now, LOG_INFO);
time_t voting_ends; tor_assert(new_voting_schedule);
/** 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. */ next_valid_after_time = new_voting_schedule->interval_starts;
int have_voted; tor_free(new_voting_schedule);
/* True iff we've requested missing votes. */
int have_fetched_missing_votes; return next_valid_after_time;
/* True iff we have built a consensus and sent the signatures around. */ }
int have_built_consensus;
/* True iff we've fetched missing signatures. */ static voting_schedule_t voting_schedule;
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};
/** Set voting_schedule to hold the timing for the next vote we should be /** Set voting_schedule to hold the timing for the next vote we should be
* doing. */ * doing. */
void void
dirvote_recalculate_timing(const or_options_t *options, time_t now) 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; int interval, vote_delay, dist_delay;
time_t start; time_t start;
time_t end; time_t end;
networkstatus_t *consensus; networkstatus_t *consensus;
voting_schedule_t *new_voting_schedule;
if (!authdir_mode_v3(options)) new_voting_schedule = tor_malloc_zero(sizeof(voting_schedule_t));
return;
consensus = networkstatus_get_live_consensus(now); consensus = networkstatus_get_live_consensus(now);
memset(&voting_schedule, 0, sizeof(voting_schedule));
if (consensus) { if (consensus) {
interval = (int)( consensus->fresh_until - consensus->valid_after ); interval = (int)( consensus->fresh_until - consensus->valid_after );
vote_delay = consensus->vote_seconds; 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) if (vote_delay + dist_delay > interval/2)
vote_delay = dist_delay = interval / 4; 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, dirvote_get_start_of_next_interval(now,interval,
options->TestingV3AuthVotingStartOffset); options->TestingV3AuthVotingStartOffset);
end = dirvote_get_start_of_next_interval(start+1, interval, 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); tor_assert(end > start);
voting_schedule.fetch_missing_signatures = start - (dist_delay/2); new_voting_schedule->fetch_missing_signatures = start - (dist_delay/2);
voting_schedule.voting_ends = start - dist_delay; new_voting_schedule->voting_ends = start - dist_delay;
voting_schedule.fetch_missing_votes = start - dist_delay - (vote_delay/2); new_voting_schedule->fetch_missing_votes = start - dist_delay - (vote_delay/2);
voting_schedule.voting_starts = start - dist_delay - vote_delay; new_voting_schedule->voting_starts = start - dist_delay - vote_delay;
{ {
char tbuf[ISO_TIME_LEN+1]; char tbuf[ISO_TIME_LEN+1];
format_iso_time(tbuf, voting_schedule.interval_starts); format_iso_time(tbuf, new_voting_schedule->interval_starts);
log_notice(LD_DIR,"Choosing expected valid-after time as %s: " tor_log(severity, LD_DIR,"Choosing expected valid-after time as %s: "
"consensus_set=%d, interval=%d", "consensus_set=%d, interval=%d",
tbuf, consensus?1:0, interval); tbuf, consensus?1:0, interval);
} }
return new_voting_schedule;
} }
/** Entry point: Take whatever voting actions are pending as of <b>now</b>. */ /** 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_publish_consensus();
dirvote_clear_votes(0); dirvote_clear_votes(0);
voting_schedule.have_published_consensus = 1; 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 /* 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. */ * signatures yet. Implement this if it turns out to ever happen. */
dirvote_recalculate_timing(options, now); 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); } 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 = tor_malloc_zero(sizeof(pending_vote_t));
pending_vote->vote_body = new_cached_dir(tor_strndup(vote_body, pending_vote->vote_body = new_cached_dir(tor_strndup(vote_body,
end_of_vote-vote_body), end_of_vote-vote_body),

View File

@ -55,7 +55,7 @@
#define MIN_SUPPORTED_CONSENSUS_METHOD 13 #define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */ /** 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 /** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */ * with no microdesc. */
@ -90,10 +90,15 @@
* ed25519 identities in microdescriptors. (Broken; see * ed25519 identities in microdescriptors. (Broken; see
* consensus_method_is_supported() for more info.) */ * consensus_method_is_supported() for more info.) */
#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21 #define MIN_METHOD_FOR_ED25519_ID_IN_MD 21
/** Lowest consensus method where authorities vote on ed25519 ids and ensure /** Lowest consensus method where authorities vote on ed25519 ids and ensure
* ed25519 id consistency. */ * ed25519 id consistency. */
#define MIN_METHOD_FOR_ED25519_ID_VOTING 22 #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 >= /** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not * MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */ * 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); authority_cert_t *authority_cert_dup(authority_cert_t *cert);
/* vote scheduling */ /* 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); void dirvote_get_preferred_voting_intervals(vote_timing_t *timing_out);
time_t dirvote_get_start_of_next_interval(time_t now, time_t dirvote_get_start_of_next_interval(time_t now,
int interval, int interval,
int offset); int offset);
void dirvote_recalculate_timing(const or_options_t *options, time_t now); void dirvote_recalculate_timing(const or_options_t *options, time_t now);
void dirvote_act(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. */ /* invoked on timers and by outside triggers. */
struct pending_vote_t * dirvote_add_vote(const char *vote_body, 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); digest_algorithm_t alg);
#ifdef DIRVOTE_PRIVATE #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, STATIC char *format_networkstatus_vote(crypto_pk_t *private_key,
networkstatus_t *v3_ns); 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); int total_authorities);
STATIC char *compute_consensus_package_lines(smartlist_t *votes); STATIC char *compute_consensus_package_lines(smartlist_t *votes);
STATIC char *make_consensus_method_list(int low, int high, const char *sep); STATIC char *make_consensus_method_list(int low, int high, const char *sep);

View File

@ -62,6 +62,8 @@ LIBTOR_A_SOURCES = \
src/or/onion.c \ src/or/onion.c \
src/or/onion_fast.c \ src/or/onion_fast.c \
src/or/onion_tap.c \ src/or/onion_tap.c \
src/or/shared_random.c \
src/or/shared_random_state.c \
src/or/transports.c \ src/or/transports.c \
src/or/periodic.c \ src/or/periodic.c \
src/or/policies.c \ src/or/policies.c \
@ -173,6 +175,8 @@ ORHEADERS = \
src/or/onion_ntor.h \ src/or/onion_ntor.h \
src/or/onion_tap.h \ src/or/onion_tap.h \
src/or/or.h \ src/or/or.h \
src/or/shared_random.h \
src/or/shared_random_state.h \
src/or/transports.h \ src/or/transports.h \
src/or/periodic.h \ src/or/periodic.h \
src/or/policies.h \ src/or/policies.h \

View File

@ -57,6 +57,7 @@
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "scheduler.h" #include "scheduler.h"
#include "shared_random.h"
#include "statefile.h" #include "statefile.h"
#include "status.h" #include "status.h"
#include "util_process.h" #include "util_process.h"
@ -2447,6 +2448,13 @@ do_main_loop(void)
cpu_init(); 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. */ /* set up once-a-second callback. */
if (! second_timer) { if (! second_timer) {
struct timeval one_second; struct timeval one_second;
@ -3214,6 +3222,9 @@ tor_cleanup(void)
accounting_record_bandwidth_usage(now, get_or_state()); accounting_record_bandwidth_usage(now, get_or_state());
or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */ or_state_mark_dirty(get_or_state(), 0); /* force an immediate save. */
or_state_save(now); or_state_save(now);
if (authdir_mode(options)) {
sr_save_and_cleanup();
}
if (authdir_mode_tests_reachability(options)) if (authdir_mode_tests_reachability(options))
rep_hist_record_mtbf_data(now, 0); rep_hist_record_mtbf_data(now, 0);
keypin_close_journal(); keypin_close_journal();
@ -3372,6 +3383,7 @@ sandbox_init_filter(void)
OPEN_DATADIR_SUFFIX("cached-extrainfo.new", ".tmp"); OPEN_DATADIR_SUFFIX("cached-extrainfo.new", ".tmp");
OPEN_DATADIR("cached-extrainfo.tmp.tmp"); OPEN_DATADIR("cached-extrainfo.tmp.tmp");
OPEN_DATADIR_SUFFIX("state", ".tmp"); OPEN_DATADIR_SUFFIX("state", ".tmp");
OPEN_DATADIR_SUFFIX("sr-state", ".tmp");
OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp"); OPEN_DATADIR_SUFFIX("unparseable-desc", ".tmp");
OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp"); OPEN_DATADIR_SUFFIX("v3-status-votes", ".tmp");
OPEN_DATADIR("key-pinning-journal"); OPEN_DATADIR("key-pinning-journal");
@ -3424,6 +3436,7 @@ sandbox_init_filter(void)
RENAME_SUFFIX("cached-extrainfo", ".new"); RENAME_SUFFIX("cached-extrainfo", ".new");
RENAME_SUFFIX("cached-extrainfo.new", ".tmp"); RENAME_SUFFIX("cached-extrainfo.new", ".tmp");
RENAME_SUFFIX("state", ".tmp"); RENAME_SUFFIX("state", ".tmp");
RENAME_SUFFIX("sr-state", ".tmp");
RENAME_SUFFIX("unparseable-desc", ".tmp"); RENAME_SUFFIX("unparseable-desc", ".tmp");
RENAME_SUFFIX("v3-status-votes", ".tmp"); RENAME_SUFFIX("v3-status-votes", ".tmp");

View File

@ -32,7 +32,9 @@
#include "router.h" #include "router.h"
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "shared_random.h"
#include "transports.h" #include "transports.h"
#include "torcert.h"
/** Map from lowercase nickname to identity digest of named server, if any. */ /** Map from lowercase nickname to identity digest of named server, if any. */
static strmap_t *named_server_map = NULL; static strmap_t *named_server_map = NULL;
@ -320,6 +322,14 @@ networkstatus_vote_free(networkstatus_t *ns)
digestmap_free(ns->desc_digest_map, NULL); 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)); memwipe(ns, 11, sizeof(*ns));
tor_free(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 /** Return the most recent consensus that we have downloaded, or NULL if we
* don't have one. */ * don't have one. */
networkstatus_t * MOCK_IMPL(networkstatus_t *,
networkstatus_get_latest_consensus(void) networkstatus_get_latest_consensus,(void))
{ {
return current_consensus; 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 /** Return the most recent consensus that we have downloaded, or NULL if it is
* no longer live. */ * no longer live. */
networkstatus_t * MOCK_IMPL(networkstatus_t *,
networkstatus_get_live_consensus(time_t now) networkstatus_get_live_consensus,(time_t now))
{ {
if (current_consensus && if (current_consensus &&
current_consensus->valid_after <= now && current_consensus->valid_after <= now &&

View File

@ -75,10 +75,10 @@ void update_certificate_downloads(time_t now);
int consensus_is_waiting_for_certs(void); int consensus_is_waiting_for_certs(void);
int client_would_use_router(const routerstatus_t *rs, time_t now, int client_would_use_router(const routerstatus_t *rs, time_t now,
const or_options_t *options); 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, MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
(consensus_flavor_t f)); (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, networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor); int flavor);
MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now)); MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now));

View File

@ -2523,6 +2523,18 @@ typedef struct networkstatus_voter_info_t {
smartlist_t *sigs; smartlist_t *sigs;
} networkstatus_voter_info_t; } 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. */ /** Enumerates the possible seriousness values of a networkstatus document. */
typedef enum { typedef enum {
NS_TYPE_VOTE, NS_TYPE_VOTE,
@ -2605,6 +2617,9 @@ typedef struct networkstatus_t {
/** If present, a map from descriptor digest to elements of /** If present, a map from descriptor digest to elements of
* routerstatus_list. */ * routerstatus_list. */
digestmap_t *desc_digest_map; digestmap_t *desc_digest_map;
/** Contains the shared random protocol data from a vote or consensus. */
networkstatus_sr_info_t sr_info;
} networkstatus_t; } networkstatus_t;
/** A set of signatures for a networkstatus consensus. Unless otherwise /** A set of signatures for a networkstatus consensus. Unless otherwise
@ -4503,6 +4518,12 @@ typedef struct {
* lifetime of this Tor process. * lifetime of this Tor process.
*/ */
uint64_t MaxUnparseableDescSizeToLog; 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; } or_options_t;
/** Persistent state for an onion router, as saved to disk. */ /** Persistent state for an onion router, as saved to disk. */

View File

@ -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 * v3 identity key hashes to <b>digest</b>, or NULL if no such authority
* is known. * is known.
*/ */
dir_server_t * MOCK_IMPL(dir_server_t *,
trusteddirserver_get_by_v3_auth_digest(const char *digest) trusteddirserver_get_by_v3_auth_digest, (const char *digest))
{ {
if (!trusted_dir_servers) if (!trusted_dir_servers)
return NULL; return NULL;

View File

@ -52,7 +52,8 @@ dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
dir_server_t *router_get_fallback_dirserver_by_digest( dir_server_t *router_get_fallback_dirserver_by_digest(
const char *digest); const char *digest);
int router_digest_is_fallback_dir(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, const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
int flags); int flags);
const routerstatus_t *router_pick_fallback_dirserver(dirinfo_type_t type, const routerstatus_t *router_pick_fallback_dirserver(dirinfo_type_t type,

View File

@ -29,6 +29,7 @@
#include "entrynodes.h" #include "entrynodes.h"
#include "torcert.h" #include "torcert.h"
#include "sandbox.h" #include "sandbox.h"
#include "shared_random.h"
#undef log #undef log
#include <math.h> #include <math.h>
@ -146,6 +147,11 @@ typedef enum {
K_CONSENSUS_METHOD, K_CONSENSUS_METHOD,
K_LEGACY_DIR_KEY, K_LEGACY_DIR_KEY,
K_DIRECTORY_FOOTER, K_DIRECTORY_FOOTER,
K_SIGNING_CERT_ED,
K_SR_FLAG,
K_COMMIT,
K_PREVIOUS_SRV,
K_CURRENT_SRV,
K_PACKAGE, K_PACKAGE,
A_PURPOSE, A_PURPOSE,
@ -447,6 +453,11 @@ static token_rule_t networkstatus_token_table[] = {
T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ), T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ),
T01("params", K_PARAMS, ARGS, NO_OBJ ), T01("params", K_PARAMS, ARGS, NO_OBJ ),
T( "fingerprint", K_FINGERPRINT, CONCAT_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 ), T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ),
CERTIFICATE_MEMBERS 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("consensus-method", K_CONSENSUS_METHOD, EQ(1), NO_OBJ),
T01("params", K_PARAMS, ARGS, 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 END_OF_TABLE
}; };
@ -3391,6 +3405,134 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
return valid; 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 /** Parse a v3 networkstatus vote, opinion, or consensus (depending on
* ns_type), from <b>s</b>, and return the result. Return NULL on failure. */ * ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
networkstatus_t * 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. */ /* Parse routerstatus lines. */
rs_tokens = smartlist_new(); rs_tokens = smartlist_new();
rs_area = memarea_new(); rs_area = memarea_new();

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
View 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

File diff suppressed because it is too large Load Diff

View 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 */

View File

@ -116,6 +116,7 @@ src_test_test_SOURCES = \
src/test/test_routerlist.c \ src/test/test_routerlist.c \
src/test/test_routerset.c \ src/test/test_routerset.c \
src/test/test_scheduler.c \ src/test/test_scheduler.c \
src/test/test_shared_random.c \
src/test/test_socks.c \ src/test/test_socks.c \
src/test/test_status.c \ src/test/test_status.c \
src/test/test_threads.c \ src/test/test_threads.c \

View 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

View File

@ -1170,6 +1170,7 @@ struct testgroup_t testgroups[] = {
{ "routerset/" , routerset_tests }, { "routerset/" , routerset_tests },
{ "scheduler/", scheduler_tests }, { "scheduler/", scheduler_tests },
{ "socks/", socks_tests }, { "socks/", socks_tests },
{ "shared-random/", sr_tests },
{ "status/" , status_tests }, { "status/" , status_tests },
{ "tortls/", tortls_tests }, { "tortls/", tortls_tests },
{ "util/", util_tests }, { "util/", util_tests },

View File

@ -225,6 +225,7 @@ extern struct testcase_t util_format_tests[];
extern struct testcase_t util_process_tests[]; extern struct testcase_t util_process_tests[];
extern struct testcase_t dns_tests[]; extern struct testcase_t dns_tests[];
extern struct testcase_t handle_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_crypto_tests[];
extern struct testcase_t slow_util_tests[]; extern struct testcase_t slow_util_tests[];

View File

@ -31,6 +31,7 @@
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "routerset.h" #include "routerset.h"
#include "shared_random_state.h"
#include "test.h" #include "test.h"
#include "test_dir_common.h" #include "test_dir_common.h"
#include "torcert.h" #include "torcert.h"
@ -1436,6 +1437,19 @@ test_dir_measured_bw_kb_cache(void *arg)
return; 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 static void
test_dir_param_voting(void *arg) test_dir_param_voting(void *arg)
{ {
@ -1545,6 +1559,43 @@ test_dir_param_voting(void *arg)
return; 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 /** 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 * same voting authority, and that they do in fact have all the same
* information. */ * information. */
@ -1789,6 +1840,15 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
return; 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 /** Run a unit tests for generating and parsing networkstatuses, with
* the supply test fns. */ * the supply test fns. */
static void static void
@ -1832,10 +1892,30 @@ test_a_networkstatus(
tt_assert(rs_test); tt_assert(rs_test);
tt_assert(vrs_test); tt_assert(vrs_test);
tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3, MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
&sign_skey_1, &sign_skey_2,
&sign_skey_3)); /* 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); 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, tt_assert(!dir_common_construct_vote_1(&vote, cert1, sign_skey_1, vrs_gen,
&v1, &n_vrs, now, 1)); &v1, &n_vrs, now, 1));
@ -5287,6 +5367,7 @@ struct testcase_t dir_tests[] = {
DIR_LEGACY(measured_bw_kb), DIR_LEGACY(measured_bw_kb),
DIR_LEGACY(measured_bw_kb_cache), DIR_LEGACY(measured_bw_kb_cache),
DIR_LEGACY(param_voting), DIR_LEGACY(param_voting),
DIR(param_voting_lookup, 0),
DIR_LEGACY(v3_networkstatus), DIR_LEGACY(v3_networkstatus),
DIR(random_weighted, 0), DIR(random_weighted, 0),
DIR(scale_bw, 0), DIR(scale_bw, 0),

View File

@ -19,13 +19,24 @@
#include "networkstatus.h" #include "networkstatus.h"
#include "nodelist.h" #include "nodelist.h"
#include "policies.h" #include "policies.h"
#include "router.h"
#include "routerlist.h" #include "routerlist.h"
#include "routerparse.h" #include "routerparse.h"
#include "shared_random.h"
#include "test.h" #include "test.h"
#include "test_dir_common.h" #include "test_dir_common.h"
void construct_consensus(char **consensus_text_md); 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 */ /* 4 digests + 3 sep + pre + post + NULL */
static char output[4*BASE64_DIGEST256_LEN+3+2+2+1]; 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));
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 */ /* No consensus available, fail early */
rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL); rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
tt_assert(rs == NULL); tt_assert(rs == NULL);

File diff suppressed because it is too large Load Diff