Browse Source

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

Trivial Conflicts:
	src/or/or.h
	src/or/routerparse.c
Nick Mathewson 7 years ago
parent
commit
aaa3129043

+ 8 - 0
changes/bug16943

@@ -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.

+ 6 - 0
doc/tor.1.txt

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

+ 21 - 0
src/common/util.c

@@ -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);
+}
+

+ 2 - 0
src/common/util.h

@@ -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);

+ 1 - 0
src/or/config.c

@@ -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"),

+ 5 - 0
src/or/directory.c

@@ -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.");
   }
 

+ 132 - 55
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,
-                                  total_authorities);
-  if (params) {
+  param_list = dirvote_compute_params(votes, consensus_method,
+                                      total_authorities);
+  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;
-
-  /* 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};
+/* 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);
+
+  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: "
-               "consensus_set=%d, interval=%d",
-               tbuf, consensus?1:0, interval);
+    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),

+ 43 - 2
src/or/dirvote.h

@@ -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);

+ 4 - 0
src/or/include.am

@@ -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				\

+ 13 - 0
src/or/main.c

@@ -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");
 

+ 14 - 4
src/or/networkstatus.c

@@ -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 &&

+ 2 - 2
src/or/networkstatus.h

@@ -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 - 0
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. */

+ 2 - 2
src/or/routerlist.c

@@ -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;

+ 2 - 1
src/or/routerlist.h

@@ -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,

+ 158 - 0
src/or/routerparse.c

@@ -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 - 0
src/or/shared_random.c

@@ -0,0 +1,1354 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random.c
+ *
+ * \brief Functions and data structure needed to accomplish the shared
+ *        random protocol as defined in proposal #250.
+ *
+ * \details
+ *
+ * This file implements the dirauth-only commit-and-reveal protocol specified
+ * by proposal #250. The protocol has two phases (sr_phase_t): the commitment
+ * phase and the reveal phase (see get_sr_protocol_phase()).
+ *
+ * During the protocol, directory authorities keep state in memory (using
+ * sr_state_t) and in disk (using sr_disk_state_t). The synchronization between
+ * these two data structures happens in disk_state_update() and
+ * disk_state_parse().
+ *
+ * Here is a rough protocol outline:
+ *
+ *      1) In the beginning of the commitment phase, dirauths generate a
+ *         commitment/reveal value for the current protocol run (see
+ *         new_protocol_run() and sr_generate_our_commit()).
+ *
+ *      2) During voting, dirauths publish their commits in their votes
+ *         depending on the current phase.  Dirauths also include the two
+ *         latest shared random values (SRV) in their votes.
+ *         (see sr_get_string_for_vote())
+ *
+ *      3) Upon receiving a commit from a vote, authorities parse it, verify
+ *         it, and attempt to save any new commitment or reveal information in
+ *         their state file (see extract_shared_random_commits() and
+ *         sr_handle_received_commits()).  They also parse SRVs from votes to
+ *         decide which SRV should be included in the final consensus (see
+ *         extract_shared_random_srvs()).
+ *
+ *      3) After voting is done, we count the SRVs we extracted from the votes,
+ *         to find the one voted by the majority of dirauths which should be
+ *         included in the final consensus (see get_majority_srv_from_votes()).
+ *         If an appropriate SRV is found, it is embedded in the consensus (see
+ *         sr_get_string_for_consensus()).
+ *
+ *      4) At the end of the reveal phase, dirauths compute a fresh SRV for the
+ *         day using the active commits (see sr_compute_srv()).  This new SRV
+ *         is embedded in the votes as described above.
+ *
+ * Some more notes:
+ *
+ * - To support rebooting authorities and to avoid double voting, each dirauth
+ *   saves the current state of the protocol on disk so that it can resume
+ *   normally in case of reboot. The disk state (sr_disk_state_t) is managed by
+ *   shared_random_state.c:state_query() and we go to extra lengths to ensure
+ *   that the state is flushed on disk everytime we receive any useful
+ *   information like commits or SRVs.
+ *
+ * - When we receive a commit from a vote, we examine it to see if it's useful
+ *   to us and whether it's appropriate to receive it according to the current
+ *   phase of the protocol (see should_keep_commit()). If the commit is useful
+ *   to us, we save it in our disk state using save_commit_to_state().  When we
+ *   receive the reveal information corresponding to a commitment, we verify
+ *   that they indeed match using verify_commit_and_reveal().
+ *
+ * - We treat consensuses as the ground truth, so everytime we generate a new
+ *   consensus we update our SR state accordingly even if our local view was
+ *   different (see sr_act_post_consensus()).
+ *
+ * - After a consensus has been composed, the SR protocol state gets prepared
+ *   for the next voting session using sr_state_update(). That function takes
+ *   care of housekeeping and also rotates the SRVs and commits in case a new
+ *   protocol run is coming up. We also call sr_state_update() on bootup (in
+ *   sr_state_init()), to prepare the state for the very first voting session.
+ *
+ * Terminology:
+ *
+ * - "Commitment" is the commitment value of the commit-and-reveal protocol.
+ *
+ * - "Reveal" is the reveal value of the commit-and-reveal protocol.
+ *
+ * - "Commit" is a struct (sr_commit_t) that contains a commitment value and
+ *    optionally also a corresponding reveal value.
+ *
+ * - "SRV" is the Shared Random Value that gets generated as the result of the
+ *   commit-and-reveal protocol.
+ **/
+
+#define SHARED_RANDOM_PRIVATE
+
+#include "or.h"
+#include "shared_random.h"
+#include "config.h"
+#include "confparse.h"
+#include "dirvote.h"
+#include "networkstatus.h"
+#include "routerkeys.h"
+#include "router.h"
+#include "routerlist.h"
+#include "shared_random_state.h"
+#include "util.h"
+
+/* String prefix of shared random values in votes/consensuses. */
+static const char previous_srv_str[] = "shared-rand-previous-value";
+static const char current_srv_str[] = "shared-rand-current-value";
+static const char commit_ns_str[] = "shared-rand-commit";
+static const char sr_flag_ns_str[] = "shared-rand-participate";
+
+/* The value of the consensus param AuthDirNumSRVAgreements found in the
+ * vote. This is set once the consensus creation subsystem requests the
+ * SRV(s) that should be put in the consensus. We use this value to decide
+ * if we keep or not an SRV. */
+static int32_t num_srv_agreements_from_vote;
+
+/* Return a heap allocated copy of the SRV <b>orig</b>. */
+STATIC sr_srv_t *
+srv_dup(const sr_srv_t *orig)
+{
+  sr_srv_t *dup = NULL;
+
+  if (!orig) {
+    return NULL;
+  }
+
+  dup = tor_malloc_zero(sizeof(sr_srv_t));
+  dup->num_reveals = orig->num_reveals;
+  memcpy(dup->value, orig->value, sizeof(dup->value));
+  return dup;
+}
+
+/* Allocate a new commit object and initializing it with <b>rsa_identity</b>
+ * that MUST be provided. The digest algorithm is set to the default one
+ * that is supported. The rest is uninitialized. This never returns NULL. */
+static sr_commit_t *
+commit_new(const char *rsa_identity)
+{
+  sr_commit_t *commit;
+
+  tor_assert(rsa_identity);
+
+  commit = tor_malloc_zero(sizeof(*commit));
+  commit->alg = SR_DIGEST_ALG;
+  memcpy(commit->rsa_identity, rsa_identity, sizeof(commit->rsa_identity));
+  return commit;
+}
+
+/* Issue a log message describing <b>commit</b>. */
+static void
+commit_log(const sr_commit_t *commit)
+{
+  tor_assert(commit);
+
+  log_debug(LD_DIR, "SR: Commit from %s", sr_commit_get_rsa_fpr(commit));
+  log_debug(LD_DIR, "SR: Commit: [TS: %" PRIu64 "] [Encoded: %s]",
+            commit->commit_ts, commit->encoded_commit);
+  log_debug(LD_DIR, "SR: Reveal: [TS: %" PRIu64 "] [Encoded: %s]",
+            commit->reveal_ts, safe_str(commit->encoded_reveal));
+}
+
+/* Make sure that the commitment and reveal information in <b>commit</b>
+ * match. If they match return 0, return -1 otherwise. This function MUST be
+ * used everytime we receive a new reveal value. Furthermore, the commit
+ * object MUST have a reveal value and the hash of the reveal value. */
+STATIC int
+verify_commit_and_reveal(const sr_commit_t *commit)
+{
+  tor_assert(commit);
+
+  log_debug(LD_DIR, "SR: Validating commit from authority %s",
+            sr_commit_get_rsa_fpr(commit));
+
+  /* Check that the timestamps match. */
+  if (commit->commit_ts != commit->reveal_ts) {
+    log_warn(LD_BUG, "SR: Commit timestamp %" PRIu64 " doesn't match reveal "
+                     "timestamp %" PRIu64, commit->commit_ts,
+             commit->reveal_ts);
+    goto invalid;
+  }
+
+  /* Verify that the hashed_reveal received in the COMMIT message, matches
+   * the reveal we just received. */
+  {
+    /* We first hash the reveal we just received. */
+    char received_hashed_reveal[sizeof(commit->hashed_reveal)];
+
+    /* Only sha3-256 is supported. */
+    if (commit->alg != SR_DIGEST_ALG) {
+      goto invalid;
+    }
+
+    /* Use the invariant length since the encoded reveal variable has an
+     * extra byte for the NUL terminated byte. */
+    if (crypto_digest256(received_hashed_reveal, commit->encoded_reveal,
+                         SR_REVEAL_BASE64_LEN, commit->alg)) {
+      /* Unable to digest the reveal blob, this is unlikely. */
+      goto invalid;
+    }
+
+    /* Now compare that with the hashed_reveal we received in COMMIT. */
+    if (fast_memneq(received_hashed_reveal, commit->hashed_reveal,
+                    sizeof(received_hashed_reveal))) {
+      log_warn(LD_BUG, "SR: Received reveal value from authority %s "
+                       "does't match the commit value.",
+               sr_commit_get_rsa_fpr(commit));
+      goto invalid;
+    }
+  }
+
+  return 0;
+ invalid:
+  return -1;
+}
+
+/* Return true iff the commit contains an encoded reveal value. */
+STATIC int
+commit_has_reveal_value(const sr_commit_t *commit)
+{
+  return !tor_mem_is_zero(commit->encoded_reveal,
+                          sizeof(commit->encoded_reveal));
+}
+
+/* Parse the encoded commit. The format is:
+ *    base64-encode( TIMESTAMP || H(REVEAL) )
+ *
+ * If successfully decoded and parsed, commit is updated and 0 is returned.
+ * On error, return -1. */
+STATIC int
+commit_decode(const char *encoded, sr_commit_t *commit)
+{
+  int decoded_len = 0;
+  size_t offset = 0;
+  /* XXX: Needs two extra bytes for the base64 decode calculation matches
+   * the binary length once decoded. #17868. */
+  char b64_decoded[SR_COMMIT_LEN + 2];
+
+  tor_assert(encoded);
+  tor_assert(commit);
+
+  if (strlen(encoded) > SR_COMMIT_BASE64_LEN) {
+    /* This means that if we base64 decode successfully the reveiced commit,
+     * we'll end up with a bigger decoded commit thus unusable. */
+    goto error;
+  }
+
+  /* Decode our encoded commit. Let's be careful here since _encoded_ is
+   * coming from the network in a dirauth vote so we expect nothing more
+   * than the base64 encoded length of a commit. */
+  decoded_len = base64_decode(b64_decoded, sizeof(b64_decoded),
+                              encoded, strlen(encoded));
+  if (decoded_len < 0) {
+    log_warn(LD_BUG, "SR: Commit from authority %s can't be decoded.",
+             sr_commit_get_rsa_fpr(commit));
+    goto error;
+  }
+
+  if (decoded_len != SR_COMMIT_LEN) {
+    log_warn(LD_BUG, "SR: Commit from authority %s decoded length doesn't "
+                     "match the expected length (%d vs %lu).",
+             sr_commit_get_rsa_fpr(commit), decoded_len, SR_COMMIT_LEN);
+    goto error;
+  }
+
+  /* First is the timestamp (8 bytes). */
+  commit->commit_ts = tor_ntohll(get_uint64(b64_decoded));
+  offset += sizeof(uint64_t);
+  /* Next is hashed reveal. */
+  memcpy(commit->hashed_reveal, b64_decoded + offset,
+         sizeof(commit->hashed_reveal));
+  /* Copy the base64 blob to the commit. Useful for voting. */
+  strlcpy(commit->encoded_commit, encoded, sizeof(commit->encoded_commit));
+
+  return 0;
+
+ error:
+  return -1;
+}
+
+/* Parse the b64 blob at <b>encoded</b> containing reveal information and
+ * store the information in-place in <b>commit</b>. Return 0 on success else
+ * a negative value. */
+STATIC int
+reveal_decode(const char *encoded, sr_commit_t *commit)
+{
+  int decoded_len = 0;
+  /* XXX: Needs two extra bytes for the base64 decode calculation matches
+   * the binary length once decoded. #17868. */
+  char b64_decoded[SR_REVEAL_LEN + 2];
+
+  tor_assert(encoded);
+  tor_assert(commit);
+
+  if (strlen(encoded) > SR_REVEAL_BASE64_LEN) {
+    /* This means that if we base64 decode successfully the received reveal
+     * value, we'll end up with a bigger decoded value thus unusable. */
+    goto error;
+  }
+
+  /* Decode our encoded reveal. Let's be careful here since _encoded_ is
+   * coming from the network in a dirauth vote so we expect nothing more
+   * than the base64 encoded length of our reveal. */
+  decoded_len = base64_decode(b64_decoded, sizeof(b64_decoded),
+                              encoded, strlen(encoded));
+  if (decoded_len < 0) {
+    log_warn(LD_BUG, "SR: Reveal from authority %s can't be decoded.",
+             sr_commit_get_rsa_fpr(commit));
+    goto error;
+  }
+
+  if (decoded_len != SR_REVEAL_LEN) {
+    log_warn(LD_BUG, "SR: Reveal from authority %s decoded length is "
+                     "doesn't match the expected length (%d vs %lu)",
+             sr_commit_get_rsa_fpr(commit), decoded_len, SR_REVEAL_LEN);
+    goto error;
+  }
+
+  commit->reveal_ts = tor_ntohll(get_uint64(b64_decoded));
+  /* Copy the last part, the random value. */
+  memcpy(commit->random_number, b64_decoded + 8,
+         sizeof(commit->random_number));
+  /* Also copy the whole message to use during verification */
+  strlcpy(commit->encoded_reveal, encoded, sizeof(commit->encoded_reveal));
+
+  return 0;
+
+ error:
+  return -1;
+}
+
+
+/* Encode a reveal element using a given commit object to dst which is a
+ * buffer large enough to put the base64-encoded reveal construction. The
+ * format is as follow:
+ *     REVEAL = base64-encode( TIMESTAMP || H(RN) )
+ * Return base64 encoded length on success else a negative value.
+ */
+STATIC int
+reveal_encode(const sr_commit_t *commit, char *dst, size_t len)
+{
+  int ret;
+  size_t offset = 0;
+  char buf[SR_REVEAL_LEN] = {0};
+
+  tor_assert(commit);
+  tor_assert(dst);
+
+  set_uint64(buf, tor_htonll(commit->reveal_ts));
+  offset += sizeof(uint64_t);
+  memcpy(buf + offset, commit->random_number,
+         sizeof(commit->random_number));
+
+  /* Let's clean the buffer and then b64 encode it. */
+  memset(dst, 0, len);
+  ret = base64_encode(dst, len, buf, sizeof(buf), 0);
+  /* Wipe this buffer because it contains our random value. */
+  memwipe(buf, 0, sizeof(buf));
+  return ret;
+}
+
+/* Encode the given commit object to dst which is a buffer large enough to
+ * put the base64-encoded commit. The format is as follow:
+ *     COMMIT = base64-encode( TIMESTAMP || H(H(RN)) )
+ * Return base64 encoded length on success else a negative value.
+ */
+STATIC int
+commit_encode(const sr_commit_t *commit, char *dst, size_t len)
+{
+  size_t offset = 0;
+  char buf[SR_COMMIT_LEN] = {0};
+
+  tor_assert(commit);
+  tor_assert(dst);
+
+  /* First is the timestamp (8 bytes). */
+  set_uint64(buf, tor_htonll(commit->commit_ts));
+  offset += sizeof(uint64_t);
+  /* and then the hashed reveal. */
+  memcpy(buf + offset, commit->hashed_reveal,
+         sizeof(commit->hashed_reveal));
+
+  /* Clean the buffer and then b64 encode it. */
+  memset(dst, 0, len);
+  return base64_encode(dst, len, buf, sizeof(buf), 0);
+}
+
+/* Cleanup both our global state and disk state. */
+static void
+sr_cleanup(void)
+{
+  sr_state_free();
+}
+
+/* Using <b>commit</b>, return a newly allocated string containing the commit
+ * information that should be used during SRV calculation. It's the caller
+ * responsibility to free the memory. Return NULL if this is not a commit to be
+ * used for SRV calculation. */
+static char *
+get_srv_element_from_commit(const sr_commit_t *commit)
+{
+  char *element;
+  tor_assert(commit);
+
+  if (!commit_has_reveal_value(commit)) {
+    return NULL;
+  }
+
+  tor_asprintf(&element, "%s%s", sr_commit_get_rsa_fpr(commit),
+               commit->encoded_reveal);
+  return element;
+}
+
+/* Return a srv object that is built with the construction:
+ *    SRV = SHA3-256("shared-random" | INT_8(reveal_num) |
+ *                   INT_4(version) | HASHED_REVEALS | previous_SRV)
+ * This function cannot fail. */
+static sr_srv_t *
+generate_srv(const char *hashed_reveals, uint64_t reveal_num,
+             const sr_srv_t *previous_srv)
+{
+  char msg[DIGEST256_LEN + SR_SRV_MSG_LEN] = {0};
+  size_t offset = 0;
+  sr_srv_t *srv;
+
+  tor_assert(hashed_reveals);
+
+  /* Add the invariant token. */
+  memcpy(msg, SR_SRV_TOKEN, SR_SRV_TOKEN_LEN);
+  offset += SR_SRV_TOKEN_LEN;
+  set_uint64(msg + offset, tor_htonll(reveal_num));
+  offset += sizeof(uint64_t);
+  set_uint32(msg + offset, htonl(SR_PROTO_VERSION));
+  offset += sizeof(uint32_t);
+  memcpy(msg + offset, hashed_reveals, DIGEST256_LEN);
+  offset += DIGEST256_LEN;
+  if (previous_srv != NULL) {
+    memcpy(msg + offset, previous_srv->value, sizeof(previous_srv->value));
+  }
+
+  /* Ok we have our message and key for the HMAC computation, allocate our
+   * srv object and do the last step. */
+  srv = tor_malloc_zero(sizeof(*srv));
+  crypto_digest256((char *) srv->value, msg, sizeof(msg), SR_DIGEST_ALG);
+  srv->num_reveals = reveal_num;
+
+  {
+    /* Debugging. */
+    char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+    sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
+    log_info(LD_DIR, "SR: Generated SRV: %s", srv_hash_encoded);
+  }
+  return srv;
+}
+
+/* Compare reveal values and return the result. This should exclusively be
+ * used by smartlist_sort(). */
+static int
+compare_reveal_(const void **_a, const void **_b)
+{
+  const sr_commit_t *a = *_a, *b = *_b;
+  return fast_memcmp(a->hashed_reveal, b->hashed_reveal,
+                     sizeof(a->hashed_reveal));
+}
+
+/* Given <b>commit</b> give the line that we should place in our votes.
+ * It's the responsibility of the caller to free the string. */
+static char *
+get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
+{
+  char *vote_line = NULL;
+
+  switch (phase) {
+  case SR_PHASE_COMMIT:
+    tor_asprintf(&vote_line, "%s %u %s %s %s\n",
+                 commit_ns_str,
+                 SR_PROTO_VERSION,
+                 crypto_digest_algorithm_get_name(commit->alg),
+                 sr_commit_get_rsa_fpr(commit),
+                 commit->encoded_commit);
+    break;
+  case SR_PHASE_REVEAL:
+  {
+    /* Send a reveal value for this commit if we have one. */
+    const char *reveal_str = commit->encoded_reveal;
+    if (tor_mem_is_zero(commit->encoded_reveal,
+                        sizeof(commit->encoded_reveal))) {
+      reveal_str = "";
+    }
+    tor_asprintf(&vote_line, "%s %u %s %s %s %s\n",
+                 commit_ns_str,
+                 SR_PROTO_VERSION,
+                 crypto_digest_algorithm_get_name(commit->alg),
+                 sr_commit_get_rsa_fpr(commit),
+                 commit->encoded_commit, reveal_str);
+    break;
+  }
+  default:
+    tor_assert(0);
+  }
+
+  log_debug(LD_DIR, "SR: Commit vote line: %s", vote_line);
+  return vote_line;
+}
+
+/* Return a heap allocated string that contains the given <b>srv</b> string
+ * representation formatted for a networkstatus document using the
+ * <b>key</b> as the start of the line. This doesn't return NULL. */
+static char *
+srv_to_ns_string(const sr_srv_t *srv, const char *key)
+{
+  char *srv_str;
+  char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+  tor_assert(srv);
+  tor_assert(key);
+
+  sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
+  tor_asprintf(&srv_str, "%s %" PRIu64 " %s\n", key,
+               srv->num_reveals, srv_hash_encoded);
+  log_debug(LD_DIR, "SR: Consensus SRV line: %s", srv_str);
+  return srv_str;
+}
+
+/* Given the previous SRV and the current SRV, return a heap allocated
+ * string with their data that could be put in a vote or a consensus. Caller
+ * must free the returned string.  Return NULL if no SRVs were provided. */
+static char *
+get_ns_str_from_sr_values(const sr_srv_t *prev_srv, const sr_srv_t *cur_srv)
+{
+  smartlist_t *chunks = NULL;
+  char *srv_str;
+
+  if (!prev_srv && !cur_srv) {
+    return NULL;
+  }
+
+  chunks = smartlist_new();
+
+  if (prev_srv) {
+    char *srv_line = srv_to_ns_string(prev_srv, previous_srv_str);
+    smartlist_add(chunks, srv_line);
+  }
+
+  if (cur_srv) {
+    char *srv_line = srv_to_ns_string(cur_srv, current_srv_str);
+    smartlist_add(chunks, srv_line);
+  }
+
+  /* Join the line(s) here in one string to return. */
+  srv_str = smartlist_join_strings(chunks, "", 0, NULL);
+  SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+  smartlist_free(chunks);
+
+  return srv_str;
+}
+
+/* Return 1 iff the two commits have the same commitment values. This
+ * function does not care about reveal values. */
+STATIC int
+commitments_are_the_same(const sr_commit_t *commit_one,
+                         const sr_commit_t *commit_two)
+{
+  tor_assert(commit_one);
+  tor_assert(commit_two);
+
+  if (strcmp(commit_one->encoded_commit, commit_two->encoded_commit)) {
+    return 0;
+  }
+  return 1;
+}
+
+/* We just received a commit from the vote of authority with
+ * <b>identity_digest</b>. Return 1 if this commit is authorititative that
+ * is, it belongs to the authority that voted it. Else return 0 if not. */
+STATIC int
+commit_is_authoritative(const sr_commit_t *commit,
+                        const char *voter_key)
+{
+  tor_assert(commit);
+  tor_assert(voter_key);
+
+  return !memcmp(commit->rsa_identity, voter_key,
+                 sizeof(commit->rsa_identity));
+}
+
+/* Decide if the newly received <b>commit</b> should be kept depending on
+ * the current phase and state of the protocol. The <b>voter_key</b> is the
+ * RSA identity key fingerprint of the authority's vote from which the
+ * commit comes from. The <b>phase</b> is the phase we should be validating
+ * the commit for. Return 1 if the commit should be added to our state or 0
+ * if not. */
+STATIC int
+should_keep_commit(const sr_commit_t *commit, const char *voter_key,
+                   sr_phase_t phase)
+{
+  const sr_commit_t *saved_commit;
+
+  tor_assert(commit);
+  tor_assert(voter_key);
+
+  log_debug(LD_DIR, "SR: Inspecting commit from %s (voter: %s)?",
+            sr_commit_get_rsa_fpr(commit),
+            hex_str(voter_key, DIGEST_LEN));
+
+  /* For a commit to be considered, it needs to be authoritative (it should
+   * be the voter's own commit). */
+  if (!commit_is_authoritative(commit, voter_key)) {
+    log_debug(LD_DIR, "SR: Ignoring non-authoritative commit.");
+    goto ignore;
+  }
+
+  /* Let's make sure, for extra safety, that this fingerprint is known to
+   * us. Even though this comes from a vote, doesn't hurt to be
+   * extracareful. */
+  if (trusteddirserver_get_by_v3_auth_digest(commit->rsa_identity) == NULL) {
+    log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized "
+                     "authority. Discarding commit.",
+             escaped(commit->rsa_identity));
+    goto ignore;
+  }
+
+  /* Check if the authority that voted for <b>commit</b> has already posted
+   * a commit before. */
+  saved_commit = sr_state_get_commit(commit->rsa_identity);
+
+  switch (phase) {
+  case SR_PHASE_COMMIT:
+    /* Already having a commit for an authority so ignore this one. */
+    if (saved_commit) {
+      /*  Receiving known commits should happen naturally since commit phase
+          lasts multiple rounds. However if the commitment value changes
+          during commit phase, it might be a bug so log more loudly. */
+      if (!commitments_are_the_same(commit, saved_commit)) {
+        log_info(LD_DIR, "SR: Received altered commit from %s in commit phase.",
+                 sr_commit_get_rsa_fpr(commit));
+      } else {
+        log_debug(LD_DIR, "SR: Ignoring known commit during commit phase.");
+      }
+      goto ignore;
+    }
+
+    /* A commit with a reveal value during commitment phase is very wrong. */
+    if (commit_has_reveal_value(commit)) {
+      log_warn(LD_DIR, "SR: Commit from authority %s has a reveal value "
+                       "during COMMIT phase. (voter: %s)",
+               sr_commit_get_rsa_fpr(commit),
+               hex_str(voter_key, DIGEST_LEN));
+      goto ignore;
+    }
+    break;
+  case SR_PHASE_REVEAL:
+    /* We are now in reveal phase. We keep a commit if and only if:
+     *
+     * - We have already seen a commit by this auth, AND
+     * - the saved commit has the same commitment value as this one, AND
+     * - the saved commit has no reveal information, AND
+     * - this commit does have reveal information, AND
+     * - the reveal & commit information are matching.
+     *
+     * If all the above are true, then we are interested in this new commit
+     * for its reveal information. */
+
+    if (!saved_commit) {
+      log_debug(LD_DIR, "SR: Ignoring commit first seen in reveal phase.");
+      goto ignore;
+    }
+
+    if (!commitments_are_the_same(commit, saved_commit)) {
+      log_warn(LD_DIR, "SR: Commit from authority %s is different from "
+                       "previous commit in our state (voter: %s)",
+               sr_commit_get_rsa_fpr(commit),
+               hex_str(voter_key, DIGEST_LEN));
+      goto ignore;
+    }
+
+    if (commit_has_reveal_value(saved_commit)) {
+      log_debug(LD_DIR, "SR: Ignoring commit with known reveal info.");
+      goto ignore;
+    }
+
+    if (!commit_has_reveal_value(commit)) {
+      log_debug(LD_DIR, "SR: Ignoring commit without reveal value.");
+      goto ignore;
+    }
+
+    if (verify_commit_and_reveal(commit) < 0) {
+      log_warn(LD_BUG, "SR: Commit from authority %s has an invalid "
+                       "reveal value. (voter: %s)",
+               sr_commit_get_rsa_fpr(commit),
+               hex_str(voter_key, DIGEST_LEN));
+      goto ignore;
+    }
+    break;
+  default:
+    tor_assert(0);
+  }
+
+  return 1;
+
+ ignore:
+  return 0;
+}
+
+/* We are in reveal phase and we found a valid and verified <b>commit</b> in
+ * a vote that contains reveal values that we could use. Update the commit
+ * we have in our state. Never call this with an unverified commit. */
+STATIC void
+save_commit_during_reveal_phase(const sr_commit_t *commit)
+{
+  sr_commit_t *saved_commit;
+
+  tor_assert(commit);
+
+  /* Get the commit from our state. */
+  saved_commit = sr_state_get_commit(commit->rsa_identity);
+  tor_assert(saved_commit);
+  /* Safety net. They can not be different commitments at this point. */
+  int same_commits = commitments_are_the_same(commit, saved_commit);
+  tor_assert(same_commits);
+
+  /* Copy reveal information to our saved commit. */
+  sr_state_copy_reveal_info(saved_commit, commit);
+}
+
+/* Save <b>commit</b> to our persistent state. Depending on the current
+ * phase, different actions are taken. Steals reference of <b>commit</b>.
+ * The commit object MUST be valid and verified before adding it to the
+ * state. */
+STATIC void
+save_commit_to_state(sr_commit_t *commit)
+{
+  sr_phase_t phase = sr_state_get_phase();
+
+  ASSERT_COMMIT_VALID(commit);
+
+  switch (phase) {
+  case SR_PHASE_COMMIT:
+    /* During commit phase, just save any new authoritative commit */
+    sr_state_add_commit(commit);
+    break;
+  case SR_PHASE_REVEAL:
+    save_commit_during_reveal_phase(commit);
+    sr_commit_free(commit);
+    break;
+  default:
+    tor_assert(0);
+  }
+}
+
+/* Return 1 if we should we keep an SRV voted by <b>n_agreements</b> auths.
+ * Return 0 if we should ignore it. */
+static int
+should_keep_srv(int n_agreements)
+{
+  /* Check if the most popular SRV has reached majority. */
+  int n_voters = get_n_authorities(V3_DIRINFO);
+  int votes_required_for_majority = (n_voters / 2) + 1;
+
+  /* We need at the very least majority to keep a value. */
+  if (n_agreements < votes_required_for_majority) {
+    log_notice(LD_DIR, "SR: SRV didn't reach majority [%d/%d]!",
+               n_agreements, votes_required_for_majority);
+    return 0;
+  }
+
+  /* When we just computed a new SRV, we need to have super majority in order
+   * to keep it. */
+  if (sr_state_srv_is_fresh()) {
+    /* Check if we have super majority for this new SRV value. */
+    if (n_agreements < num_srv_agreements_from_vote) {
+      log_notice(LD_DIR, "SR: New SRV didn't reach agreement [%d/%d]!",
+                 n_agreements, num_srv_agreements_from_vote);
+      return 0;
+    }
+  }
+
+  return 1;
+}
+
+/* Helper: compare two DIGEST256_LEN digests. */
+static int
+compare_srvs_(const void **_a, const void **_b)
+{
+  const sr_srv_t *a = *_a, *b = *_b;
+  return tor_memcmp(a->value, b->value, sizeof(a->value));
+}
+
+/* Return the most frequent member of the sorted list of DIGEST256_LEN
+ * digests in <b>sl</b> with the count of that most frequent element. */
+static sr_srv_t *
+smartlist_get_most_frequent_srv(const smartlist_t *sl, int *count_out)
+{
+  return smartlist_get_most_frequent_(sl, compare_srvs_, count_out);
+}
+
+/** Compare two SRVs. Used in smartlist sorting. */
+static int
+compare_srv_(const void **_a, const void **_b)
+{
+  const sr_srv_t *a = *_a, *b = *_b;
+  return fast_memcmp(a->value, b->value,
+                     sizeof(a->value));
+}
+
+/* Using a list of <b>votes</b>, return the SRV object from them that has
+ * been voted by the majority of dirauths. If <b>current</b> is set, we look
+ * for the current SRV value else the previous one. The returned pointer is
+ * an object located inside a vote. NULL is returned if no appropriate value
+ * could be found. */
+STATIC sr_srv_t *
+get_majority_srv_from_votes(const smartlist_t *votes, int current)
+{
+  int count = 0;
+  sr_srv_t *most_frequent_srv = NULL;
+  sr_srv_t *the_srv = NULL;
+  smartlist_t *srv_list;
+
+  tor_assert(votes);
+
+  srv_list = smartlist_new();
+
+  /* Walk over votes and register any SRVs found. */
+  SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
+    sr_srv_t *srv_tmp = NULL;
+
+    if (!v->sr_info.participate) {
+      /* Ignore vote that do not participate. */
+      continue;
+    }
+    /* Do we want previous or current SRV? */
+    srv_tmp = current ? v->sr_info.current_srv : v->sr_info.previous_srv;
+    if (!srv_tmp) {
+      continue;
+    }
+
+    smartlist_add(srv_list, srv_tmp);
+  } SMARTLIST_FOREACH_END(v);
+
+  smartlist_sort(srv_list, compare_srv_);
+  most_frequent_srv = smartlist_get_most_frequent_srv(srv_list, &count);
+  if (!most_frequent_srv) {
+    goto end;
+  }
+
+  /* Was this SRV voted by enough auths for us to keep it? */
+  if (!should_keep_srv(count)) {
+    goto end;
+  }
+
+  /* We found an SRV that we can use! Habemus SRV! */
+  the_srv = most_frequent_srv;
+
+  {
+    /* Debugging */
+    char encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+    sr_srv_encode(encoded, sizeof(encoded), the_srv);
+    log_debug(LD_DIR, "SR: Chosen SRV by majority: %s (%d votes)", encoded,
+              count);
+  }
+
+ end:
+  /* We do not free any sr_srv_t values, we don't have the ownership. */
+  smartlist_free(srv_list);
+  return the_srv;
+}
+
+/* Encode the given shared random value and put it in dst. Destination
+ * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
+void
+sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
+{
+  int ret;
+  /* Extra byte for the NULL terminated char. */
+  char buf[SR_SRV_VALUE_BASE64_LEN + 1];
+
+  tor_assert(dst);
+  tor_assert(srv);
+  tor_assert(dst_len >= sizeof(buf));
+
+  ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
+                      sizeof(srv->value), 0);
+  /* Always expect the full length without the NULL byte. */
+  tor_assert(ret == (sizeof(buf) - 1));
+  tor_assert(ret <= (int) dst_len);
+  strlcpy(dst, buf, dst_len);
+}
+
+/* Free a commit object. */
+void
+sr_commit_free(sr_commit_t *commit)
+{
+  if (commit == NULL) {
+    return;
+  }
+  /* Make sure we do not leave OUR random number in memory. */
+  memwipe(commit->random_number, 0, sizeof(commit->random_number));
+  tor_free(commit);
+}
+
+/* Generate the commitment/reveal value for the protocol run starting at
+ * <b>timestamp</b>. <b>my_rsa_cert</b> is our authority RSA certificate. */
+sr_commit_t *
+sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert)
+{
+  sr_commit_t *commit = NULL;
+  char digest[DIGEST_LEN];
+
+  tor_assert(my_rsa_cert);
+
+  /* Get our RSA identity fingerprint */
+  if (crypto_pk_get_digest(my_rsa_cert->identity_key, digest) < 0) {
+    goto error;
+  }
+
+  /* New commit with our identity key. */
+  commit = commit_new(digest);
+
+  /* Generate the reveal random value */
+  crypto_strongest_rand(commit->random_number,
+                        sizeof(commit->random_number));
+  commit->commit_ts = commit->reveal_ts = timestamp;
+
+  /* Now get the base64 blob that corresponds to our reveal */
+  if (reveal_encode(commit, commit->encoded_reveal,
+                    sizeof(commit->encoded_reveal)) < 0) {
+    log_err(LD_DIR, "SR: Unable to encode our reveal value!");
+    goto error;
+  }
+
+  /* Now let's create the commitment */
+  tor_assert(commit->alg == SR_DIGEST_ALG);
+  /* The invariant length is used here since the encoded reveal variable
+   * has an extra byte added for the NULL terminated byte. */
+  if (crypto_digest256(commit->hashed_reveal, commit->encoded_reveal,
+                       SR_REVEAL_BASE64_LEN, commit->alg)) {
+    goto error;
+  }
+
+  /* Now get the base64 blob that corresponds to our commit. */
+  if (commit_encode(commit, commit->encoded_commit,
+                    sizeof(commit->encoded_commit)) < 0) {
+    log_err(LD_DIR, "SR: Unable to encode our commit value!");
+    goto error;
+  }
+
+  log_debug(LD_DIR, "SR: Generated our commitment:");
+  commit_log(commit);
+  /* Our commit better be valid :). */
+  commit->valid = 1;
+  return commit;
+
+ error:
+  sr_commit_free(commit);
+  return NULL;
+}
+
+/* Compute the shared random value based on the active commits in our state. */
+void
+sr_compute_srv(void)
+{
+  uint64_t reveal_num = 0;
+  char *reveals = NULL;
+  smartlist_t *chunks, *commits;
+  digestmap_t *state_commits;
+
+  /* Computing a shared random value in the commit phase is very wrong. This
+   * should only happen at the very end of the reveal phase when a new
+   * protocol run is about to start. */
+  tor_assert(sr_state_get_phase() == SR_PHASE_REVEAL);
+  state_commits = sr_state_get_commits();
+
+  commits = smartlist_new();
+  chunks = smartlist_new();
+
+  /* We must make a list of commit ordered by authority fingerprint in
+   * ascending order as specified by proposal 250. */
+  DIGESTMAP_FOREACH(state_commits, key, sr_commit_t *, c) {
+    /* Extra safety net, make sure we have valid commit before using it. */
+    ASSERT_COMMIT_VALID(c);
+    /* Let's not use a commit from an authority that we don't know. It's
+     * possible that an authority could be removed during a protocol run so
+     * that commit value should never be used in the SRV computation. */
+    if (trusteddirserver_get_by_v3_auth_digest(c->rsa_identity) == NULL) {
+      log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized "
+               "authority. Discarding commit for the SRV computation.",
+               sr_commit_get_rsa_fpr(c));
+      continue;
+    }
+    /* We consider this commit valid. */
+    smartlist_add(commits, c);
+  } DIGESTMAP_FOREACH_END;
+  smartlist_sort(commits, compare_reveal_);
+
+  /* Now for each commit for that sorted list in ascending order, we'll
+   * build the element for each authority that needs to go into the srv
+   * computation. */
+  SMARTLIST_FOREACH_BEGIN(commits, const sr_commit_t *, c) {
+    char *element = get_srv_element_from_commit(c);
+    if (element) {
+      smartlist_add(chunks, element);
+      reveal_num++;
+    }
+  } SMARTLIST_FOREACH_END(c);
+  smartlist_free(commits);
+
+  {
+    /* Join all reveal values into one giant string that we'll hash so we
+     * can generated our shared random value. */
+    sr_srv_t *current_srv;
+    char hashed_reveals[DIGEST256_LEN];
+    reveals = smartlist_join_strings(chunks, "", 0, NULL);
+    SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+    smartlist_free(chunks);
+    if (crypto_digest256(hashed_reveals, reveals, strlen(reveals),
+                         SR_DIGEST_ALG)) {
+      goto end;
+    }
+    current_srv = generate_srv(hashed_reveals, reveal_num,
+                               sr_state_get_previous_srv());
+    sr_state_set_current_srv(current_srv);
+    /* We have a fresh SRV, flag our state. */
+    sr_state_set_fresh_srv();
+  }
+
+ end:
+  tor_free(reveals);
+}
+
+/* Parse a list of arguments from a SRV value either from a vote, consensus
+ * or from our disk state and return a newly allocated srv object. NULL is
+ * returned on error.
+ *
+ * The arguments' order:
+ *    num_reveals, value
+ */
+sr_srv_t *
+sr_parse_srv(const smartlist_t *args)
+{
+  char *value;
+  int ok, ret;
+  uint64_t num_reveals;
+  sr_srv_t *srv = NULL;
+
+  tor_assert(args);
+
+  if (smartlist_len(args) < 2) {
+    goto end;
+  }
+
+  /* First argument is the number of reveal values */
+  num_reveals = tor_parse_uint64(smartlist_get(args, 0),
+                                 10, 0, UINT64_MAX, &ok, NULL);
+  if (!ok) {
+    goto end;
+  }
+  /* Second and last argument is the shared random value it self. */
+  value = smartlist_get(args, 1);
+  if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
+    goto end;
+  }
+
+  srv = tor_malloc_zero(sizeof(*srv));
+  srv->num_reveals = num_reveals;
+  /* We substract one byte from the srclen because the function ignores the
+   * '=' character in the given buffer. This is broken but it's a documented
+   * behavior of the implementation. */
+  ret = base64_decode((char *) srv->value, sizeof(srv->value), value,
+                      SR_SRV_VALUE_BASE64_LEN - 1);
+  if (ret != sizeof(srv->value)) {
+    tor_free(srv);
+    srv = NULL;
+    goto end;
+  }
+ end:
+  return srv;
+}
+
+/* Parse a commit from a vote or from our disk state and return a newly
+ * allocated commit object. NULL is returned on error.
+ *
+ * The commit's data is in <b>args</b> and the order matters very much:
+ *  version, algname, RSA fingerprint, commit value[, reveal value]
+ */
+sr_commit_t *
+sr_parse_commit(const smartlist_t *args)
+{
+  uint32_t version;
+  char *value, digest[DIGEST_LEN];
+  digest_algorithm_t alg;
+  const char *rsa_identity_fpr;
+  sr_commit_t *commit = NULL;
+
+  if (smartlist_len(args) < 4) {
+    goto error;
+  }
+
+  /* First is the version number of the SR protocol which indicates at which
+   * version that commit was created. */
+  value = smartlist_get(args, 0);
+  version = (uint32_t) tor_parse_ulong(value, 10, 1, UINT32_MAX, NULL, NULL);
+  if (version > SR_PROTO_VERSION) {
+    log_info(LD_DIR, "SR: Commit version %" PRIu32 " (%s) is not supported.",
+             version, escaped(value));
+    goto error;
+  }
+
+  /* Second is the algorithm. */
+  value = smartlist_get(args, 1);
+  alg = crypto_digest_algorithm_parse_name(value);
+  if (alg != SR_DIGEST_ALG) {
+    log_warn(LD_BUG, "SR: Commit algorithm %s is not recognized.",
+             escaped(value));
+    goto error;
+  }
+
+  /* Third argument is the RSA fingerprint of the auth and turn it into a
+   * digest value. */
+  rsa_identity_fpr = smartlist_get(args, 2);
+  if (base16_decode(digest, DIGEST_LEN, rsa_identity_fpr,
+                    HEX_DIGEST_LEN) < 0) {
+    log_warn(LD_DIR, "SR: RSA fingerprint %s not decodable",
+             escaped(rsa_identity_fpr));
+    goto error;
+  }
+
+  /* Allocate commit since we have a valid identity now. */
+  commit = commit_new(digest);
+
+  /* Fourth argument is the commitment value base64-encoded. */
+  value = smartlist_get(args, 3);
+  if (commit_decode(value, commit) < 0) {
+    goto error;
+  }
+
+  /* (Optional) Fifth argument is the revealed value. */
+  if (smartlist_len(args) > 4) {
+    value = smartlist_get(args, 4);
+    if (reveal_decode(value, commit) < 0) {
+      goto error;
+    }
+  }
+
+  return commit;
+
+ error:
+  sr_commit_free(commit);
+  return NULL;
+}
+
+/* Called when we are done parsing a vote by <b>voter_key</b> that might
+ * contain some useful <b>commits</b>. Find if any of them should be kept
+ * and update our state accordingly. Once done, the list of commitments will
+ * be empty. */
+void
+sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key)
+{
+  char rsa_identity[DIGEST_LEN];
+
+  tor_assert(voter_key);
+
+  /* It's possible that the vote has _NO_ commits. */
+  if (commits == NULL) {
+    return;
+  }
+
+  /* Get the RSA identity fingerprint of this voter */
+  if (crypto_pk_get_digest(voter_key, rsa_identity) < 0) {
+    return;
+  }
+
+  SMARTLIST_FOREACH_BEGIN(commits, sr_commit_t *, commit) {
+    /* We won't need the commit in this list anymore, kept or not. */
+    SMARTLIST_DEL_CURRENT(commits, commit);
+    /* Check if this commit is valid and should be stored in our state. */
+    if (!should_keep_commit(commit, rsa_identity,
+                            sr_state_get_phase())) {
+      sr_commit_free(commit);
+      continue;
+    }
+    /* Ok, we have a valid commit now that we are about to put in our state.
+     * so flag it valid from now on. */
+    commit->valid = 1;
+    /* Everything lines up: save this commit to state then! */
+    save_commit_to_state(commit);
+  } SMARTLIST_FOREACH_END(commit);
+}
+
+/* Return a heap-allocated string containing commits that should be put in
+ * the votes. It's the responsibility of the caller to free the string.
+ * This always return a valid string, either empty or with line(s). */
+char *
+sr_get_string_for_vote(void)
+{
+  char *vote_str = NULL;
+  digestmap_t *state_commits;
+  smartlist_t *chunks = smartlist_new();
+  const or_options_t *options = get_options();
+
+  /* Are we participating in the protocol? */
+  if (!options->AuthDirSharedRandomness) {
+    goto end;
+  }
+
+  log_debug(LD_DIR, "SR: Preparing our vote info:");
+
+  /* First line, put in the vote the participation flag. */
+  {
+    char *sr_flag_line;
+    tor_asprintf(&sr_flag_line, "%s\n", sr_flag_ns_str);
+    smartlist_add(chunks, sr_flag_line);
+  }
+
+  /* In our vote we include every commitment in our permanent state. */
+  state_commits = sr_state_get_commits();
+  smartlist_t *state_commit_vote_lines = smartlist_new();
+  DIGESTMAP_FOREACH(state_commits, key, const sr_commit_t *, commit) {
+    char *line = get_vote_line_from_commit(commit, sr_state_get_phase());
+    smartlist_add(state_commit_vote_lines, line);
+  } DIGESTMAP_FOREACH_END;
+
+  /* Sort the commit strings by version (string, not numeric), algorithm,
+   * and fingerprint. This makes sure the commit lines in votes are in a
+   * recognisable, stable order. */
+  smartlist_sort_strings(state_commit_vote_lines);
+
+  /* Now add the sorted list of commits to the vote */
+  smartlist_add_all(chunks, state_commit_vote_lines);
+  smartlist_free(state_commit_vote_lines);
+
+  /* Add the SRV value(s) if any. */
+  {
+    char *srv_lines = get_ns_str_from_sr_values(sr_state_get_previous_srv(),
+                                                sr_state_get_current_srv());
+    if (srv_lines) {
+      smartlist_add(chunks, srv_lines);
+    }
+  }
+
+ end:
+  vote_str = smartlist_join_strings(chunks, "", 0, NULL);
+  SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+  smartlist_free(chunks);
+  return vote_str;
+}
+
+/* Return a heap-allocated string that should be put in the consensus and
+ * contains the shared randomness values. It's the responsibility of the
+ * caller to free the string. NULL is returned if no SRV(s) available.
+ *
+ * This is called when a consensus (any flavor) is bring created thus it
+ * should NEVER change the state nor the state should be changed in between
+ * consensus creation.
+ *
+ * <b>num_srv_agreements</b> is taken from the votes thus the voted value
+ * that should be used.
+ * */
+char *
+sr_get_string_for_consensus(const smartlist_t *votes,
+                            int32_t num_srv_agreements)
+{
+  char *srv_str;
+  const or_options_t *options = get_options();
+
+  tor_assert(votes);
+
+  /* Not participating, avoid returning anything. */
+  if (!options->AuthDirSharedRandomness) {
+    log_info(LD_DIR, "SR: Support disabled (AuthDirSharedRandomness %d)",
+             options->AuthDirSharedRandomness);
+    goto end;
+  }
+
+  /* Set the global value of AuthDirNumSRVAgreements found in the votes. */
+  num_srv_agreements_from_vote = num_srv_agreements;
+
+  /* Check the votes and figure out if SRVs should be included in the final
+   * consensus. */
+  sr_srv_t *prev_srv = get_majority_srv_from_votes(votes, 0);
+  sr_srv_t *cur_srv = get_majority_srv_from_votes(votes, 1);
+  srv_str = get_ns_str_from_sr_values(prev_srv, cur_srv);
+  if (!srv_str) {
+    goto end;
+  }
+
+  return srv_str;
+ end:
+  return NULL;
+}
+
+/* We just computed a new <b>consensus</b>. Update our state with the SRVs
+ * from the consensus (might be NULL as well). Register the SRVs in our SR
+ * state and prepare for the upcoming protocol round. */
+void
+sr_act_post_consensus(const networkstatus_t *consensus)
+{
+  time_t interval_starts;
+  const or_options_t *options = get_options();
+
+  /* Don't act if our state hasn't been initialized. We can be called during
+   * boot time when loading consensus from disk which is prior to the
+   * initialization of the SR subsystem. We also should not be doing
+   * anything if we are _not_ a directory authority and if we are a bridge
+   * authority. */
+  if (!sr_state_is_initialized() || !authdir_mode_v3(options) ||
+      authdir_mode_bridge(options)) {
+    return;
+  }
+
+  /* Set the majority voted SRVs in our state even if both are NULL. It
+   * doesn't matter this is what the majority has decided. Obviously, we can
+   * only do that if we have a consensus. */
+  if (consensus) {
+    /* Start by freeing the current SRVs since the SRVs we believed during
+     * voting do not really matter. Now that all the votes are in, we use the
+     * majority's opinion on which are the active SRVs. */
+    sr_state_clean_srvs();
+    /* Reset the fresh flag of the SRV so we know that from now on we don't
+     * have a new SRV to vote for. We just used the one from the consensus
+     * decided by the majority. */
+    sr_state_unset_fresh_srv();
+    /* Set the SR values from the given consensus. */
+    sr_state_set_previous_srv(srv_dup(consensus->sr_info.previous_srv));
+    sr_state_set_current_srv(srv_dup(consensus->sr_info.current_srv));
+  }
+
+  /* Update our internal state with the next voting interval starting time. */
+  interval_starts = get_voting_schedule(options, time(NULL),
+                                        LOG_NOTICE)->interval_starts;
+  sr_state_update(interval_starts);
+}
+
+/* Initialize shared random subsystem. This MUST be called early in the boot
+ * process of tor. Return 0 on success else -1 on error. */
+int
+sr_init(int save_to_disk)
+{
+  return sr_state_init(save_to_disk, 1);
+}
+
+/* Save our state to disk and cleanup everything. */
+void
+sr_save_and_cleanup(void)
+{
+  sr_state_save();
+  sr_cleanup();
+}
+
+#ifdef TOR_UNIT_TESTS
+
+/* Set the global value of number of SRV agreements so the test can play
+ * along by calling specific functions that don't parse the votes prior for
+ * the AuthDirNumSRVAgreements value. */
+void set_num_srv_agreements(int32_t value)
+{
+  num_srv_agreements_from_vote = value;
+}
+
+#endif /* TOR_UNIT_TESTS */

+ 166 - 0
src/or/shared_random.h

@@ -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 - 0
src/or/shared_random_state.c

@@ -0,0 +1,1353 @@
+/* Copyright (c) 2016, The Tor Project, Inc. */
+/* See LICENSE for licensing information */
+
+/**
+ * \file shared_random_state.c
+ *
+ * \brief Functions and data structures for the state of the random protocol
+ *        as defined in proposal #250.
+ **/
+
+#define SHARED_RANDOM_STATE_PRIVATE
+
+#include "or.h"
+#include "shared_random.h"
+#include "config.h"
+#include "confparse.h"
+#include "dirvote.h"
+#include "networkstatus.h"
+#include "router.h"
+#include "shared_random_state.h"
+
+/* Default filename of the shared random state on disk. */
+static const char default_fname[] = "sr-state";
+
+/* String representation of a protocol phase. */
+static const char *phase_str[] = { "unknown", "commit", "reveal" };
+
+/* Our shared random protocol state. There is only one possible state per
+ * protocol run so this is the global state which is reset at every run once
+ * the shared random value has been computed. */
+static sr_state_t *sr_state = NULL;
+
+/* Representation of our persistent state on disk. The sr_state above
+ * contains the data parsed from this state. When we save to disk, we
+ * translate the sr_state to this sr_disk_state. */
+static sr_disk_state_t *sr_disk_state = NULL;
+
+/* Disk state file keys. */
+static const char dstate_commit_key[] = "Commit";
+static const char dstate_prev_srv_key[] = "SharedRandPreviousValue";
+static const char dstate_cur_srv_key[] = "SharedRandCurrentValue";
+
+/* These next two are duplicates or near-duplicates from config.c */
+#define VAR(name, conftype, member, initvalue)                              \
+  { name, CONFIG_TYPE_ ## conftype, STRUCT_OFFSET(sr_disk_state_t, member), \
+    initvalue }
+/* As VAR, but the option name and member name are the same. */
+#define V(member, conftype, initvalue) \
+  VAR(#member, conftype, member, initvalue)
+/* Our persistent state magic number. */
+#define SR_DISK_STATE_MAGIC 0x98AB1254
+/* Each protocol phase has 12 rounds  */
+#define SHARED_RANDOM_N_ROUNDS 12
+/* Number of phase we have in a protocol. */
+#define SHARED_RANDOM_N_PHASES 2
+
+static int
+disk_state_validate_cb(void *old_state, void *state, void *default_state,
+                       int from_setconf, char **msg);
+
+/* Array of variables that are saved to disk as a persistent state. */
+static config_var_t state_vars[] = {
+  V(Version,                    UINT, "0"),
+  V(TorVersion,                 STRING, NULL),
+  V(ValidAfter,                 ISOTIME, NULL),
+  V(ValidUntil,                 ISOTIME, NULL),
+
+  V(Commit,                     LINELIST, NULL),
+
+  V(SharedRandValues,           LINELIST_V, NULL),
+  VAR("SharedRandPreviousValue",LINELIST_S, SharedRandValues, NULL),
+  VAR("SharedRandCurrentValue", LINELIST_S, SharedRandValues, NULL),
+  { NULL, CONFIG_TYPE_OBSOLETE, 0, NULL }
+};
+
+/* "Extra" variable in the state that receives lines we can't parse. This
+ * lets us preserve options from versions of Tor newer than us. */
+static config_var_t state_extra_var = {
+  "__extra", CONFIG_TYPE_LINELIST,
+  STRUCT_OFFSET(sr_disk_state_t, ExtraLines), NULL
+};
+
+/* Configuration format of sr_disk_state_t. */
+static const config_format_t state_format = {
+  sizeof(sr_disk_state_t),
+  SR_DISK_STATE_MAGIC,
+  STRUCT_OFFSET(sr_disk_state_t, magic_),
+  NULL,
+  state_vars,
+  disk_state_validate_cb,
+  &state_extra_var,
+};
+
+/* Return a string representation of a protocol phase. */
+STATIC const char *
+get_phase_str(sr_phase_t phase)
+{
+  const char *the_string = NULL;
+
+  switch (phase) {
+  case SR_PHASE_COMMIT:
+  case SR_PHASE_REVEAL:
+    the_string = phase_str[phase];
+    break;
+  default:
+    /* Unknown phase shouldn't be possible. */
+    tor_assert(0);
+  }
+
+  return the_string;
+}
+
+/* Return the voting interval of the tor vote subsystem. */
+static int
+get_voting_interval(void)
+{
+  int interval;
+  networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL));
+
+  if (consensus) {
+    interval = (int)(consensus->fresh_until - consensus->valid_after);
+  } else {
+    /* Same for both a testing and real network. We voluntarily ignore the
+     * InitialVotingInterval since it complexifies things and it doesn't
+     * affect the SR protocol. */
+    interval = get_options()->V3AuthVotingInterval;
+  }
+  tor_assert(interval > 0);
+  return interval;
+}
+
+/* Given the time <b>now</b>, return the start time of the current round of
+ * the SR protocol. For example, if it's 23:47:08, the current round thus
+ * started at 23:47:00 for a voting interval of 10 seconds. */
+static time_t
+get_start_time_of_current_round(time_t now)
+{
+  const or_options_t *options = get_options();
+  int voting_interval = get_voting_interval();
+  voting_schedule_t *new_voting_schedule =
+    get_voting_schedule(options, now, LOG_INFO);
+  tor_assert(new_voting_schedule);
+
+  /* First, get the start time of the next round */
+  time_t next_start = new_voting_schedule->interval_starts;
+  /* Now roll back next_start by a voting interval to find the start time of
+     the current round. */
+  time_t curr_start = dirvote_get_start_of_next_interval(
+                                     next_start - voting_interval - 1,
+                                     voting_interval,
+                                     options->TestingV3AuthVotingStartOffset);
+
+  tor_free(new_voting_schedule);
+
+  return curr_start;
+}
+
+/* Return the time we should expire the state file created at <b>now</b>.
+ * We expire the state file in the beginning of the next protocol run. */
+STATIC time_t
+get_state_valid_until_time(time_t now)
+{
+  int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+  int current_round, voting_interval, rounds_left;
+  time_t valid_until, beginning_of_current_round;
+
+  voting_interval = get_voting_interval();
+  /* Find the time the current round started. */
+  beginning_of_current_round = get_start_time_of_current_round(now);
+
+  /* Find how many rounds are left till the end of the protocol run */
+  current_round = (now / voting_interval) % total_rounds;
+  rounds_left = total_rounds - current_round;
+
+  /* To find the valid-until time now, take the start time of the current
+   * round and add to it the time it takes for the leftover rounds to
+   * complete. */
+  valid_until = beginning_of_current_round + (rounds_left * voting_interval);
+
+  { /* Logging */
+    char tbuf[ISO_TIME_LEN + 1];
+    format_iso_time(tbuf, valid_until);
+    log_debug(LD_DIR, "SR: Valid until time for state set to %s.", tbuf);
+  }
+
+  return valid_until;
+}
+
+/* Given the consensus 'valid-after' time, return the protocol phase we should
+ * be in. */
+STATIC sr_phase_t
+get_sr_protocol_phase(time_t valid_after)
+{
+  /* Shared random protocol has two phases, commit and reveal. */
+  int total_periods = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
+  int current_slot;
+
+  /* Split time into slots of size 'voting_interval'. See which slot we are
+   * currently into, and find which phase it corresponds to. */
+  current_slot = (valid_after / get_voting_interval()) % total_periods;
+
+  if (current_slot < SHARED_RANDOM_N_ROUNDS) {
+    return SR_PHASE_COMMIT;
+  } else {
+    return SR_PHASE_REVEAL;
+  }
+}
+
+/* Add the given <b>commit</b> to <b>state</b>. It MUST be a valid commit
+ * and there shouldn't be a commit from the same authority in the state
+ * already else verification hasn't been done prior. This takes ownership of
+ * the commit once in our state. */
+static void
+commit_add_to_state(sr_commit_t *commit, sr_state_t *state)
+{
+  sr_commit_t *saved_commit;
+
+  tor_assert(commit);
+  tor_assert(state);
+
+  saved_commit = digestmap_set(state->commits, commit->rsa_identity,
+                               commit);
+  if (saved_commit != NULL) {
+    /* This means we already have that commit in our state so adding twice
+     * the same commit is either a code flow error, a corrupted disk state
+     * or some new unknown issue. */
+    log_warn(LD_DIR, "SR: Commit from %s exists in our state while "
+                     "adding it: '%s'", sr_commit_get_rsa_fpr(commit),
+                     commit->encoded_commit);
+    sr_commit_free(saved_commit);
+  }
+}
+
+/* Helper: deallocate a commit object. (Used with digestmap_free(), which
+ * requires a function pointer whose argument is void *). */
+static void
+commit_free_(void *p)
+{
+  sr_commit_free(p);
+}
+
+/* Free a state that was allocated with state_new(). */
+static void
+state_free(sr_state_t *state)
+{
+  if (state == NULL) {
+    return;
+  }
+  tor_free(state->fname);
+  digestmap_free(state->commits, commit_free_);
+  tor_free(state->current_srv);
+  tor_free(state->previous_srv);
+  tor_free(state);
+}
+
+/* Allocate an sr_state_t object and returns it. If no <b>fname</b>, the
+ * default file name is used. This function does NOT initialize the state
+ * timestamp, phase or shared random value. NULL is never returned. */
+static sr_state_t *
+state_new(const char *fname, time_t now)
+{
+  sr_state_t *new_state = tor_malloc_zero(sizeof(*new_state));
+  /* If file name is not provided, use default. */
+  if (fname == NULL) {
+    fname = default_fname;
+  }
+  new_state->fname = tor_strdup(fname);
+  new_state->version = SR_PROTO_VERSION;
+  new_state->commits = digestmap_new();
+  new_state->phase = get_sr_protocol_phase(now);
+  new_state->valid_until = get_state_valid_until_time(now);
+  return new_state;
+}
+
+/* Set our global state pointer with the one given. */
+static void
+state_set(sr_state_t *state)
+{
+  tor_assert(state);
+  if (sr_state != NULL) {
+    state_free(sr_state);
+  }
+  sr_state = state;
+}
+
+/* Free an allocated disk state. */
+static void
+disk_state_free(sr_disk_state_t *state)
+{
+  if (state == NULL) {
+    return;
+  }
+  config_free(&state_format, state);
+}
+
+/* Allocate a new disk state, initialize it and return it. */
+static sr_disk_state_t *
+disk_state_new(time_t now)
+{
+  sr_disk_state_t *new_state = tor_malloc_zero(sizeof(*new_state));
+
+  new_state->magic_ = SR_DISK_STATE_MAGIC;
+  new_state->Version = SR_PROTO_VERSION;
+  new_state->TorVersion = tor_strdup(get_version());
+  new_state->ValidUntil = get_state_valid_until_time(now);
+  new_state->ValidAfter = now;
+
+  /* Init config format. */
+  config_init(&state_format, new_state);
+  return new_state;
+}
+
+/* Set our global disk state with the given state. */
+static void
+disk_state_set(sr_disk_state_t *state)
+{
+  tor_assert(state);
+  if (sr_disk_state != NULL) {
+    disk_state_free(sr_disk_state);
+  }
+  sr_disk_state = state;
+}
+
+/* Return -1 if the disk state is invalid (something in there that we can't or
+ * shouldn't use). Return 0 if everything checks out. */
+static int
+disk_state_validate(const sr_disk_state_t *state)
+{
+  time_t now;
+
+  tor_assert(state);
+
+  /* Do we support the protocol version in the state or is it 0 meaning
+   * Version wasn't found in the state file or bad anyway ? */
+  if (state->Version == 0 || state->Version > SR_PROTO_VERSION) {
+    goto invalid;
+  }
+
+  /* If the valid until time is before now, we shouldn't use that state. */
+  now = time(NULL);
+  if (state->ValidUntil < now) {
+    log_info(LD_DIR, "SR: Disk state has expired. Ignoring it.");
+    goto invalid;
+  }
+
+  /* Make sure we don't have a valid after time that is earlier than a valid
+   * until time which would make things not work well. */
+  if (state->ValidAfter >= state->ValidUntil) {
+    log_info(LD_DIR, "SR: Disk state valid after/until times are invalid.");
+    goto invalid;
+  }
+
+  return 0;
+
+ invalid:
+  return -1;
+}
+
+/* Validate the disk state (NOP for now). */
+static int
+disk_state_validate_cb(void *old_state, void *state, void *default_state,
+                       int from_setconf, char **msg)
+{
+  /* We don't use these; only options do. */
+  (void) from_setconf;
+  (void) default_state;
+  (void) old_state;
+
+  /* This is called by config_dump which is just before we are about to
+   * write it to disk. At that point, our global memory state has been
+   * copied to the disk state so it's fair to assume it's trustable. */
+  (void) state;
+  (void) msg;
+  return 0;
+}
+
+/* Parse the Commit line(s) in the disk state and translate them to the
+ * the memory state. Return 0 on success else -1 on error. */
+static int
+disk_state_parse_commits(sr_state_t *state,
+                         const sr_disk_state_t *disk_state)
+{
+  config_line_t *line;
+  smartlist_t *args = NULL;
+
+  tor_assert(state);
+  tor_assert(disk_state);
+
+  for (line = disk_state->Commit; line; line = line->next) {
+    sr_commit_t *commit = NULL;
+
+    /* Extra safety. */
+    if (strcasecmp(line->key, dstate_commit_key) ||
+        line->value == NULL) {
+      /* Ignore any lines that are not commits. */
+      tor_fragile_assert();
+      continue;
+    }
+    args = smartlist_new();
+    smartlist_split_string(args, line->value, " ",
+                           SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+    if (smartlist_len(args) < 3) {
+      log_warn(LD_BUG, "SR: Too few arguments in Commit Line: %s",
+               escaped(line->value));
+      goto error;
+    }
+    commit = sr_parse_commit(args);
+    if (commit == NULL) {
+      /* Ignore badly formed commit. It could also be a authority
+       * fingerprint that we don't know about so it shouldn't be used. */
+      continue;
+    }
+    /* We consider parseable commit from our disk state to be valid because
+     * they need to be in the first place to get in there. */
+    commit->valid = 1;
+    /* Add commit to our state pointer. */
+    commit_add_to_state(commit, state);
+
+    SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+    smartlist_free(args);
+  }
+
+  return 0;
+
+ error:
+  SMARTLIST_FOREACH(args, char *, cp, tor_free(cp));
+  smartlist_free(args);
+  return -1;
+}
+
+/* Parse a share random value line from the disk state and save it to dst
+ * which is an allocated srv object. Return 0 on success else -1. */
+static int
+disk_state_parse_srv(const char *value, sr_srv_t *dst)
+{
+  int ret = -1;
+  smartlist_t *args;
+  sr_srv_t *srv;
+
+  tor_assert(value);
+  tor_assert(dst);
+
+  args = smartlist_new();
+  smartlist_split_string(args, value, " ",
+                         SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+  if (smartlist_len(args) < 2) {
+    log_warn(LD_BUG, "SR: Too few arguments in shared random value. "
+             "Line: %s", escaped(value));
+    goto error;
+  }
+  srv = sr_parse_srv(args);
+  if (srv == NULL) {
+    goto error;
+  }
+  dst->num_reveals = srv->num_reveals;
+  memcpy(dst->value, srv->value, sizeof(dst->value));
+  tor_free(srv);
+  ret = 0;
+
+ error:
+  SMARTLIST_FOREACH(args, char *, s, tor_free(s));
+  smartlist_free(args);
+  return ret;
+}
+
+/* Parse both SharedRandCurrentValue and SharedRandPreviousValue line from
+ * the state. Return 0 on success else -1. */
+static int
+disk_state_parse_sr_values(sr_state_t *state,
+                           const sr_disk_state_t *disk_state)
+{
+  /* Only one value per type (current or previous) is allowed so we keep
+   * track of it with these flag. */
+  unsigned int seen_previous = 0, seen_current = 0;
+  config_line_t *line;
+  sr_srv_t *srv = NULL;
+
+  tor_assert(state);
+  tor_assert(disk_state);
+
+  for (line = disk_state->SharedRandValues; line; line = line->next) {
+    if (line->value == NULL) {
+      continue;
+    }
+    srv = tor_malloc_zero(sizeof(*srv));
+    if (disk_state_parse_srv(line->value, srv) < 0) {
+      log_warn(LD_BUG, "SR: Broken current SRV line in state %s",
+               escaped(line->value));
+      goto bad;
+    }
+    if (!strcasecmp(line->key, dstate_prev_srv_key)) {
+      if (seen_previous) {
+        log_warn(LD_DIR, "SR: Second previous SRV value seen. Bad state");
+        goto bad;
+      }
+      state->previous_srv = srv;
+      seen_previous = 1;
+    } else if (!strcasecmp(line->key, dstate_cur_srv_key)) {
+      if (seen_current) {
+        log_warn(LD_DIR, "SR: Second current SRV value seen. Bad state");
+        goto bad;
+      }
+      state->current_srv = srv;
+      seen_current = 1;
+    } else {
+      /* Unknown key. Ignoring. */
+      tor_free(srv);
+    }
+  }
+
+  return 0;
+ bad:
+  tor_free(srv);
+  return -1;
+}
+
+/* Parse the given disk state and set a newly allocated state. On success,
+ * return that state else NULL. */
+static sr_state_t *
+disk_state_parse(const sr_disk_state_t *new_disk_state)
+{
+  sr_state_t *new_state = state_new(default_fname, time(NULL));
+
+  tor_assert(new_disk_state);
+
+  new_state->version = new_disk_state->Version;
+  new_state->valid_until = new_disk_state->ValidUntil;
+  new_state->valid_after = new_disk_state->ValidAfter;
+
+  /* Set our current phase according to the valid-after time in our disk
+   * state. The disk state we are parsing contains everything for the phase
+   * starting at valid_after so make sure our phase reflects that. */
+  new_state->phase = get_sr_protocol_phase(new_state->valid_after);
+
+  /* Parse the shared random values. */
+  if (disk_state_parse_sr_values(new_state, new_disk_state) < 0) {
+    goto error;
+  }
+  /* Parse the commits. */
+  if (disk_state_parse_commits(new_state, new_disk_state) < 0) {
+    goto error;
+  }
+  /* Great! This new state contains everything we had on disk. */
+  return new_state;
+
+ error:
+  state_free(new_state);
+  return NULL;
+}
+
+/* From a valid commit object and an allocated config line, set the line's
+ * value to the state string representation of a commit. */
+static void
+disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line)
+{
+  char *reveal_str = NULL;
+
+  tor_assert(commit);
+  tor_assert(line);
+
+  if (!tor_mem_is_zero(commit->encoded_reveal,
+                       sizeof(commit->encoded_reveal))) {
+    /* Add extra whitespace so we can format the line correctly. */
+    tor_asprintf(&reveal_str, " %s", commit->encoded_reveal);
+  }
+  tor_asprintf(&line->value, "%u %s %s %s%s",
+               SR_PROTO_VERSION,
+               crypto_digest_algorithm_get_name(commit->alg),
+               sr_commit_get_rsa_fpr(commit),
+               commit->encoded_commit,
+               reveal_str != NULL ? reveal_str : "");
+  if (reveal_str != NULL) {
+    memwipe(reveal_str, 0, strlen(reveal_str));
+    tor_free(reveal_str);
+  }
+}
+
+/* From a valid srv object and an allocated config line, set the line's
+ * value to the state string representation of a shared random value. */
+static void
+disk_state_put_srv_line(const sr_srv_t *srv, config_line_t *line)
+{
+  char encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+
+  tor_assert(line);
+
+  /* No SRV value thus don't add the line. This is possible since we might
+   * not have a current or previous SRV value in our state. */
+  if (srv == NULL) {
+    return;
+  }
+  sr_srv_encode(encoded, sizeof(encoded), srv);
+  tor_asprintf(&line->value, "%" PRIu64 " %s", srv->num_reveals, encoded);
+}
+
+/* Reset disk state that is free allocated memory and zeroed the object. */
+static void
+disk_state_reset(void)
+{
+  config_free_lines(sr_disk_state->Commit);
+  config_free_lines(sr_disk_state->SharedRandValues);
+  config_free_lines(sr_disk_state->ExtraLines);
+  memset(sr_disk_state, 0, sizeof(*sr_disk_state));
+  sr_disk_state->magic_ = SR_DISK_STATE_MAGIC;
+  sr_disk_state->TorVersion = tor_strdup(get_version());
+}
+
+/* Update our disk state based on our global SR state. */
+static void
+disk_state_update(void)
+{
+  config_line_t **next, *line;
+
+  tor_assert(sr_disk_state);
+  tor_assert(sr_state);
+
+  /* Reset current disk state. */
+  disk_state_reset();
+
+  /* First, update elements that we don't need to iterate over a list to
+   * construct something. */
+  sr_disk_state->Version = sr_state->version;
+  sr_disk_state->ValidUntil = sr_state->valid_until;
+  sr_disk_state->ValidAfter = sr_state->valid_after;
+
+  /* Shared random values. */
+  next = &sr_disk_state->SharedRandValues;
+  *next = NULL;
+  if (sr_state->previous_srv != NULL) {
+    *next = line = tor_malloc_zero(sizeof(config_line_t));
+    line->key = tor_strdup(dstate_prev_srv_key);
+    disk_state_put_srv_line(sr_state->previous_srv, line);
+    next = &(line->next);
+  }
+  if (sr_state->current_srv != NULL) {
+    *next = line = tor_malloc_zero(sizeof(*line));
+    line->key = tor_strdup(dstate_cur_srv_key);
+    disk_state_put_srv_line(sr_state->current_srv, line);
+    next = &(line->next);
+  }
+
+  /* Parse the commits and construct config line(s). */
+  next = &sr_disk_state->Commit;
+  DIGESTMAP_FOREACH(sr_state->commits, key, sr_commit_t *, commit) {
+    *next = line = tor_malloc_zero(sizeof(*line));
+    line->key = tor_strdup(dstate_commit_key);
+    disk_state_put_commit_line(commit, line);
+    next = &(line->next);
+  } DIGESTMAP_FOREACH_END;
+}
+
+/* Load state from disk and put it into our disk state. If the state passes
+ * validation, our global state will be updated with it. Return 0 on
+ * success. On error, -EINVAL is returned if the state on disk did contained
+ * something malformed or is unreadable. -ENOENT is returned indicating that
+ * the state file is either empty of non existing. */
+static int
+disk_state_load_from_disk(void)
+{
+  int ret;
+  char *fname;
+
+  fname = get_datadir_fname(default_fname);
+  ret = disk_state_load_from_disk_impl(fname);
+  tor_free(fname);
+
+  return ret;
+}
+
+/* Helper for disk_state_load_from_disk(). */
+STATIC int
+disk_state_load_from_disk_impl(const char *fname)
+{
+  int ret;
+  char *content = NULL;
+  sr_state_t *parsed_state = NULL;
+  sr_disk_state_t *disk_state = NULL;
+
+  /* Read content of file so we can parse it. */
+  if ((content = read_file_to_str(fname, 0, NULL)) == NULL) {
+    log_warn(LD_FS, "SR: Unable to read SR state file %s",
+             escaped(fname));
+    ret = -errno;
+    goto error;
+  }
+
+  {
+    config_line_t *lines = NULL;
+    char *errmsg = NULL;
+
+    /* Every error in this code path will return EINVAL. */
+    ret = -EINVAL;
+    if (config_get_lines(content, &lines, 0) < 0) {
+      config_free_lines(lines);
+      goto error;
+    }
+
+    disk_state = disk_state_new(time(NULL));
+    config_assign(&state_format, disk_state, lines, 0, 0, &errmsg);
+    config_free_lines(lines);
+    if (errmsg) {
+      log_warn(LD_DIR, "SR: Reading state error: %s", errmsg);
+      tor_free(errmsg);
+      goto error;
+    }
+  }
+
+  /* So far so good, we've loaded our state file into our disk state. Let's
+   * validate it and then parse it. */
+  if (disk_state_validate(disk_state) < 0) {
+    ret = -EINVAL;
+    goto error;
+  }
+
+  parsed_state = disk_state_parse(disk_state);
+  if (parsed_state == NULL) {
+    ret = -EINVAL;
+    goto error;
+  }
+  state_set(parsed_state);
+  disk_state_set(disk_state);
+  tor_free(content);
+  log_info(LD_DIR, "SR: State loaded successfully from file %s", fname);
+  return 0;
+
+ error:
+  disk_state_free(disk_state);
+  tor_free(content);
+  return ret;
+}
+
+/* Save the disk state to disk but before that update it from the current
+ * state so we always have the latest. Return 0 on success else -1. */
+static int
+disk_state_save_to_disk(void)
+{
+  int ret;
+  char *state, *content = NULL, *fname = NULL;
+  char tbuf[ISO_TIME_LEN + 1];
+  time_t now = time(NULL);
+
+  /* If we didn't have the opportunity to setup an internal disk state,
+   * don't bother saving something to disk. */
+  if (sr_disk_state == NULL) {
+    ret = 0;
+    goto done;
+  }
+
+  /* Make sure that our disk state is up to date with our memory state
+   * before saving it to disk. */
+  disk_state_update();
+  state = config_dump(&state_format, NULL, sr_disk_state, 0, 0);
+  format_local_iso_time(tbuf, now);
+  tor_asprintf(&content,
+               "# Tor shared random state file last generated on %s "
+               "local time\n"
+               "# Other times below are in UTC\n"
+               "# Please *do not* edit this file.\n\n%s",
+               tbuf, state);
+  tor_free(state);
+  fname = get_datadir_fname(default_fname);
+  if (write_str_to_file(fname, content, 0) < 0) {
+    log_warn(LD_FS, "SR: Unable to write SR state to file %s", fname);
+    ret = -1;
+    goto done;
+  }
+  ret = 0;
+  log_debug(LD_DIR, "SR: Saved state to file %s", fname);
+
+ done:
+  tor_free(fname);
+  tor_free(content);
+  return ret;
+}
+
+/* Reset our state to prepare for a new protocol run. Once this returns, all
+ * commits in the state will be removed and freed. */
+STATIC void
+reset_state_for_new_protocol_run(time_t valid_after)
+{
+  tor_assert(sr_state);
+
+  /* Keep counters in track */
+  sr_state->n_reveal_rounds = 0;
+  sr_state->n_commit_rounds = 0;
+  sr_state->n_protocol_runs++;
+
+  /* Reset valid-until */
+  sr_state->valid_until = get_state_valid_until_time(valid_after);
+  sr_state->valid_after = valid_after;
+
+  /* We are in a new protocol run so cleanup commits. */
+  sr_state_delete_commits();
+}
+
+/* This is the first round of the new protocol run starting at
+ * <b>valid_after</b>. Do the necessary housekeeping. */
+STATIC void
+new_protocol_run(time_t valid_after)
+{
+  sr_commit_t *our_commitment = NULL;
+
+  /* Only compute the srv at the end of the reveal phase. */
+  if (sr_state->phase == SR_PHASE_REVEAL) {
+    /* We are about to compute a new shared random value that will be set in
+     * our state as the current value so rotate values. */
+    state_rotate_srv();
+    /* Compute the shared randomness value of the day. */
+    sr_compute_srv();
+  }
+
+  /* Prepare for the new protocol run by reseting the state */
+  reset_state_for_new_protocol_run(valid_after);
+
+  /* Do some logging */
+  log_info(LD_DIR, "SR: Protocol run #%" PRIu64 " starting!",
+           sr_state->n_protocol_runs);
+
+  /* Generate fresh commitments for this protocol run */
+  our_commitment = sr_generate_our_commit(valid_after,
+                                          get_my_v3_authority_cert());
+  if (our_commitment) {
+    /* Add our commitment to our state. In case we are unable to create one
+     * (highly unlikely), we won't vote for this protocol run since our
+     * commitment won't be in our state. */
+    sr_state_add_commit(our_commitment);
+  }
+}
+
+/* Return 1 iff the <b>next_phase</b> is a phase transition from the current
+ * phase that is it's different. */
+STATIC int
+is_phase_transition(sr_phase_t next_phase)
+{
+  return sr_state->phase != next_phase;
+}
+
+/* Helper function: return a commit using the RSA fingerprint of the
+ * authority or NULL if no such commit is known. */
+static sr_commit_t *
+state_query_get_commit(const char *rsa_fpr)
+{
+  tor_assert(rsa_fpr);
+  return digestmap_get(sr_state->commits, rsa_fpr);
+}
+
+/* Helper function: This handles the GET state action using an
+ * <b>obj_type</b> and <b>data</b> needed for the action. */
+static void *
+state_query_get_(sr_state_object_t obj_type, const void *data)
+{
+  void *obj = NULL;
+
+  switch (obj_type) {
+  case SR_STATE_OBJ_COMMIT:
+  {
+    obj = state_query_get_commit(data);
+    break;
+  }
+  case SR_STATE_OBJ_COMMITS:
+    obj = sr_state->commits;
+    break;
+  case SR_STATE_OBJ_CURSRV:
+    obj = sr_state->current_srv;
+    break;
+  case SR_STATE_OBJ_PREVSRV:
+    obj = sr_state->previous_srv;
+    break;
+  case SR_STATE_OBJ_PHASE:
+    obj = &sr_state->phase;
+    break;
+  case SR_STATE_OBJ_VALID_AFTER:
+  default:
+    tor_assert(0);
+  }
+  return obj;
+}
+
+/* Helper function: This handles the PUT state action using an
+ * <b>obj_type</b> and <b>data</b> needed for the action. */
+static void
+state_query_put_(sr_state_object_t obj_type, void *data)
+{
+  switch (obj_type) {
+  case SR_STATE_OBJ_COMMIT:
+  {
+    sr_commit_t *commit = data;
+    tor_assert(commit);
+    commit_add_to_state(commit, sr_state);
+    break;
+  }
+  case SR_STATE_OBJ_CURSRV:
+    sr_state->current_srv = (sr_srv_t *) data;
+    break;
+  case SR_STATE_OBJ_PREVSRV:
+    sr_state->previous_srv = (sr_srv_t *) data;
+    break;
+  case SR_STATE_OBJ_VALID_AFTER:
+    sr_state->valid_after = *((time_t *) data);
+    break;
+  /* It's not allowed to change the phase nor the full commitments map from
+   * the state. The phase is decided during a strict process post voting and
+   * the commits should be put individually. */
+  case SR_STATE_OBJ_PHASE:
+  case SR_STATE_OBJ_COMMITS:
+  default:
+    tor_assert(0);
+  }
+}
+
+/* Helper function: This handles the DEL_ALL state action using an
+ * <b>obj_type</b> and <b>data</b> needed for the action. */
+static void
+state_query_del_all_(sr_state_object_t obj_type)
+{
+  switch (obj_type) {
+  case SR_STATE_OBJ_COMMIT:
+  {
+    /* We are in a new protocol run so cleanup commitments. */
+    DIGESTMAP_FOREACH_MODIFY(sr_state->commits, key, sr_commit_t *, c) {
+      sr_commit_free(c);
+      MAP_DEL_CURRENT(key);
+    } DIGESTMAP_FOREACH_END;
+    break;
+  }
+  /* The following object are _NOT_ suppose to be removed. */
+  case SR_STATE_OBJ_CURSRV:
+  case SR_STATE_OBJ_PREVSRV:
+  case SR_STATE_OBJ_PHASE:
+  case SR_STATE_OBJ_COMMITS:
+  case SR_STATE_OBJ_VALID_AFTER:
+  default:
+    tor_assert(0);
+  }
+}
+
+/* Helper function: This handles the DEL state action using an
+ * <b>obj_type</b> and <b>data</b> needed for the action. */
+static void
+state_query_del_(sr_state_object_t obj_type, void *data)
+{
+  (void) data;
+
+  switch (obj_type) {
+  case SR_STATE_OBJ_PREVSRV:
+    tor_free(sr_state->previous_srv);
+    break;
+  case SR_STATE_OBJ_CURSRV:
+    tor_free(sr_state->current_srv);
+    break;
+  case SR_STATE_OBJ_COMMIT:
+  case SR_STATE_OBJ_COMMITS:
+  case SR_STATE_OBJ_PHASE:
+  case SR_STATE_OBJ_VALID_AFTER:
+  default:
+    tor_assert(0);
+  }
+}
+
+/* Query state using an <b>action</b> for an object type <b>obj_type</b>.
+ * The <b>data</b> pointer needs to point to an object that the action needs
+ * to use and if anything is required to be returned, it is stored in
+ * <b>out</b>.
+ *
+ * This mechanism exists so we have one single point where we synchronized
+ * our memory state with our disk state for every actions that changes it.
+ * We then trigger a write on disk immediately.
+ *
+ * This should be the only entry point to our memory state. It's used by all
+ * our state accessors and should be in the future. */
+static void
+state_query(sr_state_action_t action, sr_state_object_t obj_type,
+            void *data, void **out)
+{
+  switch (action) {
+  case SR_STATE_ACTION_GET:
+    *out = state_query_get_(obj_type, data);
+    break;
+  case SR_STATE_ACTION_PUT:
+    state_query_put_(obj_type, data);
+    break;
+  case SR_STATE_ACTION_DEL:
+    state_query_del_(obj_type, data);
+    break;
+  case SR_STATE_ACTION_DEL_ALL:
+    state_query_del_all_(obj_type);
+    break;
+  case SR_STATE_ACTION_SAVE:
+    /* Only trigger a disk state save. */
+    break;
+  default:
+    tor_assert(0);
+  }
+
+  /* If the action actually changes the state, immediately save it to disk.
+   * The following will sync the state -> disk state and then save it. */
+  if (action != SR_STATE_ACTION_GET) {
+    disk_state_save_to_disk();
+  }
+}
+
+/* Delete the current SRV value from the state freeing it and the value is set
+ * to NULL meaning empty. */
+static void
+state_del_current_srv(void)
+{
+  state_query(SR_STATE_ACTION_DEL, SR_STATE_OBJ_CURSRV, NULL, NULL);
+}
+
+/* Delete the previous SRV value from the state freeing it and the value is
+ * set to NULL meaning empty. */
+static void
+state_del_previous_srv(void)
+{
+  state_query(SR_STATE_ACTION_DEL, SR_STATE_OBJ_PREVSRV, NULL, NULL);
+}
+
+/* Rotate SRV value by freeing the previous value, assigning the current
+ * value to the previous one and nullifying the current one. */
+STATIC void
+state_rotate_srv(void)
+{
+  /* First delete previous SRV from the state. Object will be freed. */
+  state_del_previous_srv();
+  /* Set previous SRV with the current one. */
+  sr_state_set_previous_srv(sr_state_get_current_srv());
+  /* Nullify the current srv. */
+  sr_state_set_current_srv(NULL);
+}
+
+/* Set valid after time in the our state. */
+void
+sr_state_set_valid_after(time_t valid_after)
+{
+  state_query(SR_STATE_ACTION_PUT, SR_STATE_OBJ_VALID_AFTER,
+              (void *) &valid_after, NULL);
+}
+
+/* Return the phase we are currently in according to our state. */
+sr_phase_t
+sr_state_get_phase(void)
+{
+  void *ptr;
+  state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_PHASE, NULL, &ptr);
+  return *(sr_phase_t *) ptr;
+}
+
+/* Return the previous SRV value from our state. Value CAN be NULL. */
+const sr_srv_t *
+sr_state_get_previous_srv(void)
+{
+  const sr_srv_t *srv;
+  state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_PREVSRV, NULL,
+              (void *) &srv);
+  return srv;
+}
+
+/* Set the current SRV value from our state. Value CAN be NULL. The srv
+ * object ownership is transfered to the state object. */
+void
+sr_state_set_previous_srv(const sr_srv_t *srv)
+{
+  state_query(SR_STATE_ACTION_PUT, SR_STATE_OBJ_PREVSRV, (void *) srv,
+              NULL);
+}
+
+/* Return the current SRV value from our state. Value CAN be NULL. */
+const sr_srv_t *
+sr_state_get_current_srv(void)
+{
+  const sr_srv_t *srv;
+  state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_CURSRV, NULL,
+              (void *) &srv);
+  return srv;
+}
+
+/* Set the current SRV value from our state. Value CAN be NULL. The srv
+ * object ownership is transfered to the state object. */
+void
+sr_state_set_current_srv(const sr_srv_t *srv)
+{
+  state_query(SR_STATE_ACTION_PUT, SR_STATE_OBJ_CURSRV, (void *) srv,
+              NULL);
+}
+
+/* Clean all the SRVs in our state. */
+void
+sr_state_clean_srvs(void)
+{
+  /* Remove SRVs from state. They will be set to NULL as "empty". */
+  state_del_previous_srv();
+  state_del_current_srv();
+}
+
+/* Return a pointer to the commits map from our state. CANNOT be NULL. */
+digestmap_t *
+sr_state_get_commits(void)
+{
+  digestmap_t *commits;
+  state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_COMMITS,
+              NULL, (void *) &commits);
+  tor_assert(commits);
+  return commits;
+}
+
+/* Update the current SR state as needed for the upcoming voting round at
+ * <b>valid_after</b>. */
+void
+sr_state_update(time_t valid_after)
+{
+  sr_phase_t next_phase;
+
+  tor_assert(sr_state);
+
+  /* Don't call this function twice in the same voting period. */
+  if (valid_after <= sr_state->valid_after) {
+    log_info(LD_DIR, "SR: Asked to update state twice. Ignoring.");
+    return;
+  }
+
+  /* Get phase of upcoming round. */
+  next_phase = get_sr_protocol_phase(valid_after);
+
+  /* If we are transitioning to a new protocol phase, prepare the stage. */
+  if (is_phase_transition(next_phase)) {
+    if (next_phase == SR_PHASE_COMMIT) {
+      /* Going into commit phase means we are starting a new protocol run. */
+      new_protocol_run(valid_after);
+    }
+    /* Set the new phase for this round */
+    sr_state->phase = next_phase;
+  } else if (sr_state->phase == SR_PHASE_COMMIT &&
+             digestmap_size(sr_state->commits) == 0) {
+    /* We are _NOT_ in a transition phase so if we are in the commit phase
+     * and have no commit, generate one. Chances are that we are booting up
+     * so let's have a commit in our state for the next voting period. */
+    sr_commit_t *our_commit =
+      sr_generate_our_commit(valid_after, get_my_v3_authority_cert());
+    if (our_commit) {
+      /* Add our commitment to our state. In case we are unable to create one
+       * (highly unlikely), we won't vote for this protocol run since our
+       * commitment won't be in our state. */
+      sr_state_add_commit(our_commit);
+    }
+  }
+
+  sr_state_set_valid_after(valid_after);
+
+  /* Count the current round */
+  if (sr_state->phase == SR_PHASE_COMMIT) {
+    /* invariant check: we've not entered reveal phase yet */
+    tor_assert(sr_state->n_reveal_rounds == 0);
+    sr_state->n_commit_rounds++;
+  } else {
+    sr_state->n_reveal_rounds++;
+  }
+
+  { /* Debugging. */
+    char tbuf[ISO_TIME_LEN + 1];
+    format_iso_time(tbuf, valid_after);
+    log_info(LD_DIR, "SR: State prepared for upcoming voting period (%s). "
+             "Upcoming phase is %s (counters: %d commit & %d reveal rounds).",
+             tbuf, get_phase_str(sr_state->phase),
+             sr_state->n_commit_rounds, sr_state->n_reveal_rounds);
+  }
+}
+
+/* Return commit object from the given authority digest <b>rsa_identity</b>.
+ * Return NULL if not found. */
+sr_commit_t *
+sr_state_get_commit(const char *rsa_identity)
+{
+  sr_commit_t *commit;
+
+  tor_assert(rsa_identity);
+
+  state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_COMMIT,
+              (void *) rsa_identity, (void *) &commit);
+  return commit;
+}
+
+/* Add <b>commit</b> to the permanent state. The commit object ownership is
+ * transfered to the state so the caller MUST not free it. */
+void
+sr_state_add_commit(sr_commit_t *commit)
+{
+  tor_assert(commit);
+
+  /* Put the commit to the global state. */
+  state_query(SR_STATE_ACTION_PUT, SR_STATE_OBJ_COMMIT,
+              (void *) commit, NULL);
+
+  log_debug(LD_DIR, "SR: Commit from %s has been added to our state.",
+            sr_commit_get_rsa_fpr(commit));
+}
+
+/* Remove all commits from our state. */
+void
+sr_state_delete_commits(void)
+{
+  state_query(SR_STATE_ACTION_DEL_ALL, SR_STATE_OBJ_COMMIT, NULL, NULL);
+}
+
+/* Copy the reveal information from <b>commit</b> into <b>saved_commit</b>.
+ * This <b>saved_commit</b> MUST come from our current SR state. Once modified,
+ * the disk state is updated. */
+void
+sr_state_copy_reveal_info(sr_commit_t *saved_commit, const sr_commit_t *commit)
+{
+  tor_assert(saved_commit);
+  tor_assert(commit);
+
+  saved_commit->reveal_ts = commit->reveal_ts;
+  memcpy(saved_commit->random_number, commit->random_number,
+         sizeof(saved_commit->random_number));
+
+  strlcpy(saved_commit->encoded_reveal, commit->encoded_reveal,
+          sizeof(saved_commit->encoded_reveal));
+  state_query(SR_STATE_ACTION_SAVE, 0, NULL, NULL);
+  log_debug(LD_DIR, "SR: Reveal value learned %s (for commit %s) from %s",
+            saved_commit->encoded_reveal, saved_commit->encoded_commit,
+            sr_commit_get_rsa_fpr(saved_commit));
+}
+
+/* Set the fresh SRV flag from our state. This doesn't need to trigger a
+ * disk state synchronization so we directly change the state. */
+void
+sr_state_set_fresh_srv(void)
+{
+  sr_state->is_srv_fresh = 1;
+}
+
+/* Unset the fresh SRV flag from our state. This doesn't need to trigger a
+ * disk state synchronization so we directly change the state. */
+void
+sr_state_unset_fresh_srv(void)
+{
+  sr_state->is_srv_fresh = 0;
+}
+
+/* Return the value of the fresh SRV flag. */
+unsigned int
+sr_state_srv_is_fresh(void)
+{
+  return sr_state->is_srv_fresh;
+}
+
+/* Cleanup and free our disk and memory state. */
+void
+sr_state_free(void)
+{
+  state_free(sr_state);
+  disk_state_free(sr_disk_state);
+  /* Nullify our global state. */
+  sr_state = NULL;
+  sr_disk_state = NULL;
+}
+
+/* Save our current state in memory to disk. */
+void
+sr_state_save(void)
+{
+  /* Query a SAVE action on our current state so it's synced and saved. */
+  state_query(SR_STATE_ACTION_SAVE, 0, NULL, NULL);
+}
+
+/* Return 1 iff the state has been initialized that is it exists in memory.
+ * Return 0 otherwise. */
+int
+sr_state_is_initialized(void)
+{
+  return sr_state == NULL ? 0 : 1;
+}
+
+/* Initialize the disk and memory state.
+ *
+ * If save_to_disk is set to 1, the state is immediately saved to disk after
+ * creation else it's not thus only kept in memory.
+ * If read_from_disk is set to 1, we try to load the state from the disk and
+ * if not found, a new state is created.
+ *
+ * Return 0 on success else a negative value on error. */
+int
+sr_state_init(int save_to_disk, int read_from_disk)
+{
+  int ret = -ENOENT;
+  time_t now = time(NULL);
+
+  /* We shouldn't have those assigned. */
+  tor_assert(sr_disk_state == NULL);
+  tor_assert(sr_state == NULL);
+
+  /* First, try to load the state from disk. */
+  if (read_from_disk) {
+    ret = disk_state_load_from_disk();
+  }
+
+  if (ret < 0) {
+    switch (-ret) {
+    case EINVAL:
+      /* We have a state on disk but it contains something we couldn't parse
+       * or an invalid entry in the state file. Let's remove it since it's
+       * obviously unusable and replace it by an new fresh state below. */
+    case ENOENT:
+      {
+        /* No state on disk so allocate our states for the first time. */
+        sr_state_t *new_state = state_new(default_fname, now);
+        sr_disk_state_t *new_disk_state = disk_state_new(now);
+        state_set(new_state);
+        /* It's important to set our disk state pointer since the save call
+         * below uses it to synchronized it with our memory state.  */
+        disk_state_set(new_disk_state);
+        /* No entry, let's save our new state to disk. */
+        if (save_to_disk && disk_state_save_to_disk() < 0) {
+          goto error;
+        }
+        break;
+      }
+    default:
+      /* Big problem. Not possible. */
+      tor_assert(0);
+    }
+  }
+  /* We have a state in memory, let's make sure it's updated for the current
+   * and next voting round. */
+  {
+    time_t valid_after = get_next_valid_after_time(now);
+    sr_state_update(valid_after);
+  }
+  return 0;
+
+ error:
+  return -1;
+}
+
+#ifdef TOR_UNIT_TESTS
+
+/* Set the current phase of the protocol. Used only by unit tests. */
+void
+set_sr_phase(sr_phase_t phase)
+{
+  tor_assert(sr_state);
+  sr_state->phase = phase;
+}
+
+/* Get the SR state. Used only by unit tests */
+sr_state_t *
+get_sr_state(void)
+{
+  return sr_state;
+}
+
+#endif /* TOR_UNIT_TESTS */

+ 146 - 0
src/or/shared_random_state.h

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

+ 1 - 0
src/test/include.am

@@ -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 - 0
src/test/sr_srv_calc_ref.py

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

+ 1 - 0
src/test/test.c

@@ -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 },

+ 1 - 0
src/test/test.h

@@ -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[];

+ 84 - 3
src/test/test_dir.c

@@ -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),

+ 17 - 0
src/test/test_routerlist.c

@@ -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 - 0
src/test/test_shared_random.c

@@ -0,0 +1,1261 @@
+#define SHARED_RANDOM_PRIVATE
+#define SHARED_RANDOM_STATE_PRIVATE
+#define CONFIG_PRIVATE
+#define DIRVOTE_PRIVATE
+
+#include "or.h"
+#include "test.h"
+#include "config.h"
+#include "dirvote.h"
+#include "shared_random.h"
+#include "shared_random_state.h"
+#include "routerkeys.h"
+#include "routerlist.h"
+#include "router.h"
+#include "routerparse.h"
+#include "networkstatus.h"
+
+static authority_cert_t *mock_cert;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+  tor_assert(mock_cert);
+  return mock_cert;
+}
+
+static dir_server_t ds;
+
+static dir_server_t *
+trusteddirserver_get_by_v3_auth_digest_m(const char *digest)
+{
+  (void) digest;
+  /* The shared random code only need to know if a valid pointer to a dir
+   * server object has been found so this is safe because it won't use the
+   * pointer at all never. */
+  return &ds;
+}
+
+/* Setup a minimal dirauth environment by initializing the SR state and
+ * making sure the options are set to be an authority directory. */
+static void
+init_authority_state(void)
+{
+  MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+
+  or_options_t *options = get_options_mutable();
+  mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+  tt_assert(mock_cert);
+  options->AuthoritativeDir = 1;
+  tt_int_op(0, ==, load_ed_keys(options, time(NULL)));
+  sr_state_init(0, 0);
+  /* It's possible a commit has been generated in our state depending on
+   * the phase we are currently in which uses "now" as the starting
+   * timestamp. Delete it before we do any testing below. */
+  sr_state_delete_commits();
+
+ done:
+  UNMOCK(get_my_v3_authority_cert);
+}
+
+static void
+test_get_sr_protocol_phase(void *arg)
+{
+  time_t the_time;
+  sr_phase_t phase;
+  int retval;
+
+  (void) arg;
+
+  /* Initialize SR state */
+  init_authority_state();
+
+  {
+    retval = parse_rfc1123_time("Wed, 20 Apr 2015 23:59:00 UTC", &the_time);
+    tt_int_op(retval, ==, 0);
+
+    phase = get_sr_protocol_phase(the_time);
+    tt_int_op(phase, ==, SR_PHASE_REVEAL);
+  }
+
+  {
+    retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:00 UTC", &the_time);
+    tt_int_op(retval, ==, 0);
+
+    phase = get_sr_protocol_phase(the_time);
+    tt_int_op(phase, ==, SR_PHASE_COMMIT);
+  }
+
+  {
+    retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:01 UTC", &the_time);
+    tt_int_op(retval, ==, 0);
+
+    phase = get_sr_protocol_phase(the_time);
+    tt_int_op(phase, ==, SR_PHASE_COMMIT);
+  }
+
+  {
+    retval = parse_rfc1123_time("Wed, 20 Apr 2015 11:59:00 UTC", &the_time);
+    tt_int_op(retval, ==, 0);
+
+    phase = get_sr_protocol_phase(the_time);
+    tt_int_op(phase, ==, SR_PHASE_COMMIT);
+  }
+
+  {
+    retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:00 UTC", &the_time);
+    tt_int_op(retval, ==, 0);
+
+    phase = get_sr_protocol_phase(the_time);
+    tt_int_op(phase, ==, SR_PHASE_REVEAL);
+  }
+
+  {
+    retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:01 UTC", &the_time);
+    tt_int_op(retval, ==, 0);
+
+    phase = get_sr_protocol_phase(the_time);
+    tt_int_op(phase, ==, SR_PHASE_REVEAL);
+  }
+
+  {
+    retval = parse_rfc1123_time("Wed, 20 Apr 2015 13:00:00 UTC", &the_time);
+    tt_int_op(retval, ==, 0);
+
+    phase = get_sr_protocol_phase(the_time);
+    tt_int_op(phase, ==, SR_PHASE_REVEAL);
+  }
+
+ done:
+  ;
+}
+
+static networkstatus_t *mock_consensus = NULL;
+
+static void
+test_get_state_valid_until_time(void *arg)
+{
+  time_t current_time;
+  time_t valid_until_time;
+  char tbuf[ISO_TIME_LEN + 1];
+  int retval;
+
+  (void) arg;
+
+  {
+    /* Get the valid until time if called at 00:00:01 */
+    retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC",
+                                &current_time);
+    tt_int_op(retval, ==, 0);
+    valid_until_time = get_state_valid_until_time(current_time);
+
+    /* Compare it with the correct result */
+    format_iso_time(tbuf, valid_until_time);
+    tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+  }
+
+  {
+    retval = parse_rfc1123_time("Mon, 20 Apr 2015 19:22:00 UTC",
+                                &current_time);
+    tt_int_op(retval, ==, 0);
+    valid_until_time = get_state_valid_until_time(current_time);
+
+    format_iso_time(tbuf, valid_until_time);
+    tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+  }
+
+  {
+    retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:00 UTC",
+                                &current_time);
+    tt_int_op(retval, ==, 0);
+    valid_until_time = get_state_valid_until_time(current_time);
+
+    format_iso_time(tbuf, valid_until_time);
+    tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+  }
+
+  {
+    retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+                                &current_time);
+    tt_int_op(retval, ==, 0);
+    valid_until_time = get_state_valid_until_time(current_time);
+
+    format_iso_time(tbuf, valid_until_time);
+    tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+  }
+
+ done:
+  ;
+}
+
+/* Mock function to immediately return our local 'mock_consensus'. */
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+  (void) now;
+  return mock_consensus;
+}
+
+/** Test the get_next_valid_after_time() function. */
+static void
+test_get_next_valid_after_time(void *arg)
+{
+  time_t current_time;
+  time_t valid_after_time;
+  char tbuf[ISO_TIME_LEN + 1];
+  int retval;
+
+  (void) arg;
+
+  {
+    /* Setup a fake consensus just to get the times out of it, since
+       get_next_valid_after_time() needs them. */
+    mock_consensus = tor_malloc_zero(sizeof(networkstatus_t));
+
+    retval = parse_rfc1123_time("Mon, 13 Jan 2016 16:00:00 UTC",
+                                &mock_consensus->fresh_until);
+    tt_int_op(retval, ==, 0);
+
+    retval = parse_rfc1123_time("Mon, 13 Jan 2016 15:00:00 UTC",
+                                &mock_consensus->valid_after);
+    tt_int_op(retval, ==, 0);
+
+    MOCK(networkstatus_get_live_consensus,
+         mock_networkstatus_get_live_consensus);
+  }
+
+  {
+    /* Get the valid after time if called at 00:00:00 */
+    retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+                                &current_time);
+    tt_int_op(retval, ==, 0);
+    valid_after_time = get_next_valid_after_time(current_time);
+
+    /* Compare it with the correct result */
+    format_iso_time(tbuf, valid_after_time);
+    tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf);
+  }
+
+  {
+    /* Get the valid until time if called at 00:00:01 */
+    retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC",
+                                &current_time);
+    tt_int_op(retval, ==, 0);
+    valid_after_time = get_next_valid_after_time(current_time);
+
+    /* Compare it with the correct result */
+    format_iso_time(tbuf, valid_after_time);
+    tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf);
+ }
+
+  {
+    retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:30:01 UTC",
+                                &current_time);
+    tt_int_op(retval, ==, 0);
+    valid_after_time = get_next_valid_after_time(current_time);
+
+    /* Compare it with the correct result */
+    format_iso_time(tbuf, valid_after_time);
+    tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ done:
+  networkstatus_vote_free(mock_consensus);
+}
+
+/* In this test we are going to generate a sr_commit_t object and validate
+ * it. We first generate our values, and then we parse them as if they were
+ * received from the network. After we parse both the commit and the reveal,
+ * we verify that they indeed match. */
+static void
+test_sr_commit(void *arg)
+{
+  authority_cert_t *auth_cert = NULL;
+  time_t now = time(NULL);
+  sr_commit_t *our_commit = NULL;
+  smartlist_t *args = smartlist_new();
+
+  (void) arg;
+
+  {  /* Setup a minimal dirauth environment for this test  */
+    or_options_t *options = get_options_mutable();
+
+    auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+    tt_assert(auth_cert);
+
+    options->AuthoritativeDir = 1;
+    tt_int_op(0, ==, load_ed_keys(options, now));
+  }
+
+  /* Generate our commit object and validate it has the appropriate field
+   * that we can then use to build a representation that we'll find in a
+   * vote coming from the network. */
+  {
+    sr_commit_t test_commit;
+    our_commit = sr_generate_our_commit(now, auth_cert);
+    tt_assert(our_commit);
+    /* Default and only supported algorithm for now. */
+    tt_assert(our_commit->alg == DIGEST_SHA3_256);
+    /* We should have a reveal value. */
+    tt_assert(commit_has_reveal_value(our_commit));
+    /* We should have a random value. */
+    tt_assert(!tor_mem_is_zero((char *) our_commit->random_number,
+                               sizeof(our_commit->random_number)));
+    /* Commit and reveal timestamp should be the same. */
+    tt_int_op(our_commit->commit_ts, ==, our_commit->reveal_ts);
+    /* We should have a hashed reveal. */
+    tt_assert(!tor_mem_is_zero(our_commit->hashed_reveal,
+                               sizeof(our_commit->hashed_reveal)));
+    /* Do we have a valid encoded commit and reveal. Note the following only
+     * tests if the generated values are correct. Their could be a bug in
+     * the decode function but we test them seperately. */
+    tt_int_op(0, ==, reveal_decode(our_commit->encoded_reveal,
+                                   &test_commit));
+    tt_int_op(0, ==, commit_decode(our_commit->encoded_commit,
+                                   &test_commit));
+    tt_int_op(0, ==, verify_commit_and_reveal(our_commit));
+  }
+
+  /* Let's make sure our verify commit and reveal function works. We'll
+   * make it fail a bit with known failure case. */
+  {
+    /* Copy our commit so we don't alter it for the rest of testing. */
+    sr_commit_t test_commit;
+    memcpy(&test_commit, our_commit, sizeof(test_commit));
+
+    /* Timestamp MUST match. */
+    test_commit.commit_ts = test_commit.reveal_ts - 42;
+    tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit));
+    memcpy(&test_commit, our_commit, sizeof(test_commit));
+    tt_int_op(0, ==, verify_commit_and_reveal(&test_commit));
+
+    /* Hashed reveal must match the H(encoded_reveal). */
+    memset(test_commit.hashed_reveal, 'X',
+           sizeof(test_commit.hashed_reveal));
+    tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit));
+    memcpy(&test_commit, our_commit, sizeof(test_commit));
+    tt_int_op(0, ==, verify_commit_and_reveal(&test_commit));
+  }
+
+  /* We'll build a list of values from our commit that our parsing function
+   * takes from a vote line and see if we can parse it correctly. */
+  {
+    sr_commit_t *parsed_commit;
+    smartlist_add(args, tor_strdup("1"));
+    smartlist_add(args,
+               tor_strdup(crypto_digest_algorithm_get_name(our_commit->alg)));
+    smartlist_add(args, tor_strdup(sr_commit_get_rsa_fpr(our_commit)));
+    smartlist_add(args, our_commit->encoded_commit);
+    smartlist_add(args, our_commit->encoded_reveal);
+    parsed_commit = sr_parse_commit(args);
+    tt_assert(parsed_commit);
+    /* That parsed commit should be _EXACTLY_ like our original commit (we
+     * have to explicitly set the valid flag though). */
+    parsed_commit->valid = 1;
+    tt_mem_op(parsed_commit, OP_EQ, our_commit, sizeof(*parsed_commit));
+    /* Cleanup */
+    tor_free(smartlist_get(args, 0)); /* strdup here. */
+    tor_free(smartlist_get(args, 1)); /* strdup here. */
+    smartlist_clear(args);
+    sr_commit_free(parsed_commit);
+  }
+
+ done:
+  smartlist_free(args);
+  sr_commit_free(our_commit);
+}
+
+/* Test the encoding and decoding function for commit and reveal values. */
+static void
+test_encoding(void *arg)
+{
+  (void) arg;
+  int ret, duper_rand = 42;
+  /* Random number is 32 bytes. */
+  char raw_rand[32];
+  time_t ts = 1454333590;
+  char hashed_rand[DIGEST256_LEN], hashed_reveal[DIGEST256_LEN];
+  sr_commit_t parsed_commit;
+
+  /* Encoded commit is: base64-encode( 1454333590 || H(H(42)) ). Remember
+   * that we do no expose the raw bytes of our PRNG to the network thus
+   * explaining the double H(). */
+  static const char *encoded_commit =
+    "AAAAAFavXpZbx2LRneYFSLPCP8DLp9BXfeH5FXzbkxM4iRXKGeA54g==";
+  /* Encoded reveal is: base64-encode( 1454333590 || H(42) ). */
+  static const char *encoded_reveal =
+    "AAAAAFavXpYk9x9kTjiQWUqjHwSAEOdPAfCaurXgjPy173SzYjeC2g==";
+
+  /* Set up our raw random bytes array. */
+  memset(raw_rand, 0, sizeof(raw_rand));
+  memcpy(raw_rand, &duper_rand, sizeof(duper_rand));
+  /* Hash random number. */
+  ret = crypto_digest256(hashed_rand, raw_rand,
+                         sizeof(raw_rand), SR_DIGEST_ALG);
+  tt_int_op(0, ==, ret);
+  /* Hash reveal value. */
+  tt_int_op(SR_REVEAL_BASE64_LEN, ==, strlen(encoded_reveal));
+  ret = crypto_digest256(hashed_reveal, encoded_reveal,
+                         strlen(encoded_reveal), SR_DIGEST_ALG);
+  tt_int_op(0, ==, ret);
+  tt_int_op(SR_COMMIT_BASE64_LEN, ==, strlen(encoded_commit));
+
+  /* Test our commit/reveal decode functions. */
+  {
+    /* Test the reveal encoded value. */
+    tt_int_op(0, ==, reveal_decode(encoded_reveal, &parsed_commit));
+    tt_uint_op(ts, ==, parsed_commit.reveal_ts);
+    tt_mem_op(hashed_rand, OP_EQ, parsed_commit.random_number,
+              sizeof(hashed_rand));
+
+    /* Test the commit encoded value. */
+    memset(&parsed_commit, 0, sizeof(parsed_commit));
+    tt_int_op(0, ==, commit_decode(encoded_commit, &parsed_commit));
+    tt_uint_op(ts, ==, parsed_commit.commit_ts);
+    tt_mem_op(encoded_commit, OP_EQ, parsed_commit.encoded_commit,
+              sizeof(parsed_commit.encoded_commit));
+    tt_mem_op(hashed_reveal, OP_EQ, parsed_commit.hashed_reveal,
+              sizeof(hashed_reveal));
+  }
+
+  /* Test our commit/reveal encode functions. */
+  {
+    /* Test the reveal encode. */
+    char encoded[SR_REVEAL_BASE64_LEN + 1];
+    parsed_commit.reveal_ts = ts;
+    memcpy(parsed_commit.random_number, hashed_rand,
+           sizeof(parsed_commit.random_number));
+    ret = reveal_encode(&parsed_commit, encoded, sizeof(encoded));
+    tt_int_op(SR_REVEAL_BASE64_LEN, ==, ret);
+    tt_mem_op(encoded_reveal, OP_EQ, encoded, strlen(encoded_reveal));
+  }
+
+  {
+    /* Test the commit encode. */
+    char encoded[SR_COMMIT_BASE64_LEN + 1];
+    parsed_commit.commit_ts = ts;
+    memcpy(parsed_commit.hashed_reveal, hashed_reveal,
+           sizeof(parsed_commit.hashed_reveal));
+    ret = commit_encode(&parsed_commit, encoded, sizeof(encoded));
+    tt_int_op(SR_COMMIT_BASE64_LEN, ==, ret);
+    tt_mem_op(encoded_commit, OP_EQ, encoded, strlen(encoded_commit));
+  }
+
+ done:
+  ;
+}
+
+/** Setup some SRVs in our SR state. If <b>also_current</b> is set, then set
+ *  both current and previous SRVs.
+ *  Helper of test_vote() and test_sr_compute_srv(). */
+static void
+test_sr_setup_srv(int also_current)
+{
+  sr_srv_t *srv = tor_malloc_zero(sizeof(sr_srv_t));
+  srv->num_reveals = 42;
+  memcpy(srv->value,
+         "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
+         sizeof(srv->value));
+
+ sr_state_set_previous_srv(srv);
+
+ if (also_current) {
+   srv = tor_malloc_zero(sizeof(sr_srv_t));
+   srv->num_reveals = 128;
+   memcpy(srv->value,
+          "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN",
+          sizeof(srv->value));
+
+   sr_state_set_current_srv(srv);
+ }
+}
+
+/* Test anything that has to do with SR protocol and vote. */
+static void
+test_vote(void *arg)
+{
+  int ret;
+  time_t now = time(NULL);
+  sr_commit_t *our_commit = NULL;
+
+  (void) arg;
+
+  MOCK(trusteddirserver_get_by_v3_auth_digest,
+       trusteddirserver_get_by_v3_auth_digest_m);
+
+  {  /* Setup a minimal dirauth environment for this test  */
+    init_authority_state();
+    /* Set ourself in reveal phase so we can parse the reveal value in the
+     * vote as well. */
+    set_sr_phase(SR_PHASE_REVEAL);
+  }
+
+  /* Generate our commit object and validate it has the appropriate field
+   * that we can then use to build a representation that we'll find in a
+   * vote coming from the network. */
+  {
+    sr_commit_t *saved_commit;
+    our_commit = sr_generate_our_commit(now, mock_cert);
+    tt_assert(our_commit);
+    sr_state_add_commit(our_commit);
+    /* Make sure it's there. */
+    saved_commit = sr_state_get_commit(our_commit->rsa_identity);
+    tt_assert(saved_commit);
+  }
+
+  /* Also setup the SRVs */
+  test_sr_setup_srv(1);
+
+  { /* Now test the vote generation */
+    smartlist_t *chunks = smartlist_new();
+    smartlist_t *tokens = smartlist_new();
+    /* Get our vote line and validate it. */
+    char *lines = sr_get_string_for_vote();
+    tt_assert(lines);
+    /* Split the lines. We expect 2 here. */
+    ret = smartlist_split_string(chunks, lines, "\n", SPLIT_IGNORE_BLANK, 0);
+    tt_int_op(ret, ==, 4);
+    tt_str_op(smartlist_get(chunks, 0), OP_EQ, "shared-rand-participate");
+    /* Get our commitment line and will validate it agains our commit. The
+     * format is as follow:
+     * "shared-rand-commitment" SP version SP algname SP identity
+     *                          SP COMMIT [SP REVEAL] NL
+     */
+    char *commit_line = smartlist_get(chunks, 1);
+    tt_assert(commit_line);
+    ret = smartlist_split_string(tokens, commit_line, " ", 0, 0);
+    tt_int_op(ret, ==, 6);
+    tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-commit");
+    tt_str_op(smartlist_get(tokens, 1), OP_EQ, "1");
+    tt_str_op(smartlist_get(tokens, 2), OP_EQ,
+              crypto_digest_algorithm_get_name(DIGEST_SHA3_256));
+    char digest[DIGEST_LEN];
+    base16_decode(digest, sizeof(digest), smartlist_get(tokens, 3),
+                  HEX_DIGEST_LEN);
+    tt_mem_op(digest, ==, our_commit->rsa_identity, sizeof(digest));
+    tt_str_op(smartlist_get(tokens, 4), OP_EQ, our_commit->encoded_commit);
+    tt_str_op(smartlist_get(tokens, 5), OP_EQ, our_commit->encoded_reveal);
+
+    /* Finally, does this vote line creates a valid commit object? */
+    smartlist_t *args = smartlist_new();
+    smartlist_add(args, smartlist_get(tokens, 1));
+    smartlist_add(args, smartlist_get(tokens, 2));
+    smartlist_add(args, smartlist_get(tokens, 3));
+    smartlist_add(args, smartlist_get(tokens, 4));
+    smartlist_add(args, smartlist_get(tokens, 5));
+    sr_commit_t *parsed_commit = sr_parse_commit(args);
+    tt_assert(parsed_commit);
+    /* Set valid flag explicitly here to compare since it's not set by
+     * simply parsing the commit. */
+    parsed_commit->valid = 1;
+    tt_mem_op(parsed_commit, ==, our_commit, sizeof(*our_commit));
+
+    /* minor cleanup */
+    SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+    smartlist_clear(tokens);
+
+    /* Now test the previous SRV */
+    char *prev_srv_line = smartlist_get(chunks, 2);
+    tt_assert(prev_srv_line);
+    ret = smartlist_split_string(tokens, prev_srv_line, " ", 0, 0);
+    tt_int_op(ret, ==, 3);
+    tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-previous-value");
+    tt_str_op(smartlist_get(tokens, 1), OP_EQ, "42");
+    tt_str_op(smartlist_get(tokens, 2), OP_EQ,
+           "WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlo=");
+
+    /* minor cleanup */
+    SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+    smartlist_clear(tokens);
+
+    /* Now test the current SRV */
+    char *current_srv_line = smartlist_get(chunks, 3);
+    tt_assert(current_srv_line);
+    ret = smartlist_split_string(tokens, current_srv_line, " ", 0, 0);
+    tt_int_op(ret, ==, 3);
+    tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-current-value");
+    tt_str_op(smartlist_get(tokens, 1), OP_EQ, "128");
+    tt_str_op(smartlist_get(tokens, 2), OP_EQ,
+           "Tk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk4=");
+
+    /* Clean up */
+    sr_commit_free(parsed_commit);
+    SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+    smartlist_free(chunks);
+    SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+    smartlist_free(tokens);
+    smartlist_clear(args);
+    smartlist_free(args);
+  }
+
+ done:
+  sr_commit_free(our_commit);
+  UNMOCK(trusteddirserver_get_by_v3_auth_digest);
+}
+
+const char *sr_state_str = "Version 1\n"
+  "TorVersion 0.2.9.0-alpha-dev\n"
+  "ValidAfter 2037-04-19 07:16:00\n"
+  "ValidUntil 2037-04-20 07:16:00\n"
+  "Commit 1 sha3-256 FA3CEC2C99DC68D3166B9B6E4FA21A4026C2AB1C "
+      "7M8GdubCAAdh7WUG0DiwRyxTYRKji7HATa7LLJEZ/UAAAAAAVmfUSg== "
+      "AAAAAFZn1EojfIheIw42bjK3VqkpYyjsQFSbv/dxNna3Q8hUEPKpOw==\n"
+  "Commit 1 sha3-256 41E89EDFBFBA44983E21F18F2230A4ECB5BFB543 "
+     "17aUsYuMeRjd2N1r8yNyg7aHqRa6gf4z7QPoxxAZbp0AAAAAVmfUSg==\n"
+  "Commit 1 sha3-256 36637026573A04110CF3E6B1D201FB9A98B88734 "
+     "DDDYtripvdOU+XPEUm5xpU64d9IURSds1xSwQsgeB8oAAAAAVmfUSg==\n"
+  "SharedRandPreviousValue 4 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=\n"
+  "SharedRandCurrentValue 3 8dWeW12KEzTGEiLGgO1UVJ7Z91CekoRcxt6Q9KhnOFI=\n";
+
+/** Create an SR disk state, parse it and validate that the parsing went
+ *  well. Yes! */
+static void
+test_state_load_from_disk(void *arg)
+{
+  int ret;
+  char *dir = tor_strdup(get_fname("test_sr_state"));
+  char *sr_state_path = tor_strdup(get_fname("test_sr_state/sr_state"));
+  sr_state_t *the_sr_state = NULL;
+
+  (void) arg;
+
+  MOCK(trusteddirserver_get_by_v3_auth_digest,
+       trusteddirserver_get_by_v3_auth_digest_m);
+
+  /* First try with a nonexistent path. */
+  ret = disk_state_load_from_disk_impl("NONEXISTENTNONEXISTENT");
+  tt_assert(ret == -ENOENT);
+
+  /* Now create a mock state directory and state file */
+#ifdef _WIN32
+  ret = mkdir(dir);
+#else
+  ret = mkdir(dir, 0700);
+#endif
+  tt_assert(ret == 0);
+  ret = write_str_to_file(sr_state_path, sr_state_str, 0);
+  tt_assert(ret == 0);
+
+  /* Try to load the directory itself. Should fail. */
+  ret = disk_state_load_from_disk_impl(dir);
+  tt_assert(ret == -EISDIR);
+
+  /* State should be non-existent at this point. */
+  the_sr_state = get_sr_state();
+  tt_assert(!the_sr_state);
+
+  /* Now try to load the correct file! */
+  ret = disk_state_load_from_disk_impl(sr_state_path);
+  tt_assert(ret == 0);
+
+  /* Check the content of the state */
+  /* XXX check more deeply!!! */
+  the_sr_state = get_sr_state();
+  tt_assert(the_sr_state);
+  tt_assert(the_sr_state->version == 1);
+  tt_assert(digestmap_size(the_sr_state->commits) == 3);
+  tt_assert(the_sr_state->current_srv);
+  tt_assert(the_sr_state->current_srv->num_reveals == 3);
+  tt_assert(the_sr_state->previous_srv);
+
+  /* XXX Now also try loading corrupted state files and make sure parsing
+     fails */
+
+ done:
+  tor_free(dir);
+  tor_free(sr_state_path);
+  UNMOCK(trusteddirserver_get_by_v3_auth_digest);
+}
+
+/** Generate three specially crafted commits (based on the test
+ *  vector at sr_srv_calc_ref.py). Helper of test_sr_compute_srv(). */
+static void
+test_sr_setup_commits(void)
+{
+  time_t now = time(NULL);
+  sr_commit_t *commit_a, *commit_b, *commit_c, *commit_d;
+  sr_commit_t *place_holder = tor_malloc_zero(sizeof(*place_holder));
+  authority_cert_t *auth_cert = NULL;
+
+  {  /* Setup a minimal dirauth environment for this test  */
+    or_options_t *options = get_options_mutable();
+
+    auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+    tt_assert(auth_cert);
+
+    options->AuthoritativeDir = 1;
+    tt_int_op(0, ==, load_ed_keys(options, now));
+  }
+
+  /* Generate three dummy commits according to sr_srv_calc_ref.py .  Then
+     register them to the SR state. Also register a fourth commit 'd' with no
+     reveal info, to make sure that it will get ignored during SRV
+     calculation. */
+
+  { /* Commit from auth 'a' */
+    commit_a = sr_generate_our_commit(now, auth_cert);
+    tt_assert(commit_a);
+
+    /* Do some surgery on the commit */
+    memset(commit_a->rsa_identity, 'A', sizeof(commit_a->rsa_identity));
+    strlcpy(commit_a->encoded_reveal,
+            "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+            sizeof(commit_a->encoded_reveal));
+    memcpy(commit_a->hashed_reveal,
+           "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+           sizeof(commit_a->hashed_reveal));
+  }
+
+  { /* Commit from auth 'b' */
+    commit_b = sr_generate_our_commit(now, auth_cert);
+    tt_assert(commit_b);
+
+    /* Do some surgery on the commit */
+    memset(commit_b->rsa_identity, 'B', sizeof(commit_b->rsa_identity));
+    strlcpy(commit_b->encoded_reveal,
+            "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
+          sizeof(commit_b->encoded_reveal));
+    memcpy(commit_b->hashed_reveal,
+           "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
+           sizeof(commit_b->hashed_reveal));
+  }
+
+  { /* Commit from auth 'c' */
+    commit_c = sr_generate_our_commit(now, auth_cert);
+    tt_assert(commit_c);
+
+    /* Do some surgery on the commit */
+    memset(commit_c->rsa_identity, 'C', sizeof(commit_c->rsa_identity));
+    strlcpy(commit_c->encoded_reveal,
+            "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
+            sizeof(commit_c->encoded_reveal));
+    memcpy(commit_c->hashed_reveal,
+           "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
+           sizeof(commit_c->hashed_reveal));
+  }
+
+  { /* Commit from auth 'd' */
+    commit_d = sr_generate_our_commit(now, auth_cert);
+    tt_assert(commit_d);
+
+    /* Do some surgery on the commit */
+    memset(commit_d->rsa_identity, 'D', sizeof(commit_d->rsa_identity));
+    strlcpy(commit_d->encoded_reveal,
+            "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
+            sizeof(commit_d->encoded_reveal));
+    memcpy(commit_d->hashed_reveal,
+           "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
+           sizeof(commit_d->hashed_reveal));
+    /* Clean up its reveal info */
+    memcpy(place_holder, commit_d, sizeof(*place_holder));
+    memset(commit_d->encoded_reveal, 0, sizeof(commit_d->encoded_reveal));
+    tt_assert(!commit_has_reveal_value(commit_d));
+  }
+
+  /* Register commits to state (during commit phase) */
+  set_sr_phase(SR_PHASE_COMMIT);
+  save_commit_to_state(commit_a);
+  save_commit_to_state(commit_b);
+  save_commit_to_state(commit_c);
+  save_commit_to_state(commit_d);
+  tt_int_op(digestmap_size(get_sr_state()->commits), ==, 4);
+
+  /* Now during REVEAL phase save commit D by restoring its reveal. */
+  set_sr_phase(SR_PHASE_REVEAL);
+  save_commit_to_state(place_holder);
+  tt_str_op(commit_d->encoded_reveal, OP_EQ,
+            "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
+  /* Go back to an empty encoded reveal value. */
+  memset(commit_d->encoded_reveal, 0, sizeof(commit_d->encoded_reveal));
+  memset(commit_d->random_number, 0, sizeof(commit_d->random_number));
+  tt_assert(!commit_has_reveal_value(commit_d));
+
+ done:
+  return;
+}
+
+/** Verify that the SRV generation procedure is proper by testing it against
+ *  the test vector from ./sr_srv_calc_ref.py. */
+static void
+test_sr_compute_srv(void *arg)
+{
+  (void) arg;
+  const sr_srv_t *current_srv = NULL;
+
+#define SRV_TEST_VECTOR \
+  "2A9B1D6237DAB312A40F575DA85C147663E7ED3F80E9555395F15B515C74253D"
+
+  MOCK(trusteddirserver_get_by_v3_auth_digest,
+       trusteddirserver_get_by_v3_auth_digest_m);
+
+  init_authority_state();
+
+  /* Setup the commits for this unittest */
+  test_sr_setup_commits();
+  test_sr_setup_srv(0);
+
+  /* Now switch to reveal phase */
+  set_sr_phase(SR_PHASE_REVEAL);
+
+  /* Compute the SRV */
+  sr_compute_srv();
+
+  /* Check the result against the test vector */
+  current_srv = sr_state_get_current_srv();
+  tt_assert(current_srv);
+  tt_int_op(current_srv->num_reveals, ==, 3);
+  tt_str_op(hex_str((char*)current_srv->value, 32),
+            ==,
+            SRV_TEST_VECTOR);
+
+ done:
+  UNMOCK(trusteddirserver_get_by_v3_auth_digest);
+}
+
+/** Return a minimal vote document with a current SRV value set to
+ *  <b>srv</b>. */
+static networkstatus_t *
+get_test_vote_with_curr_srv(const char *srv)
+{
+  networkstatus_t *vote = tor_malloc_zero(sizeof(networkstatus_t));
+
+  vote->type = NS_TYPE_VOTE;
+  vote->sr_info.participate = 1;
+  vote->sr_info.current_srv = tor_malloc_zero(sizeof(sr_srv_t));
+  vote->sr_info.current_srv->num_reveals = 42;
+  memcpy(vote->sr_info.current_srv->value,
+         srv,
+         sizeof(vote->sr_info.current_srv->value));
+
+  return vote;
+}
+
+/* Test the function that picks the right SRV given a bunch of votes. Make sure
+ * that the function returns an SRV iff the majority/agreement requirements are
+ * met. */
+static void
+test_sr_get_majority_srv_from_votes(void *arg)
+{
+  sr_srv_t *chosen_srv;
+  smartlist_t *votes = smartlist_new();
+
+#define SRV_1 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+#define SRV_2 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+
+  (void) arg;
+
+  init_authority_state();
+  /* Make sure our SRV is fresh so we can consider the super majority with
+   * the consensus params of number of agreements needed. */
+  sr_state_set_fresh_srv();
+
+  /* The test relies on the dirauth list being initialized. */
+  clear_dir_servers();
+  add_default_trusted_dir_authorities(V3_DIRINFO);
+  tt_int_op(get_n_authorities(V3_DIRINFO), ==, 9);
+
+  { /* Prepare voting environment with just a single vote. */
+    networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
+    smartlist_add(votes, vote);
+  }
+
+  /* Since it's only one vote with an SRV, it should not achieve majority and
+     hence no SRV will be returned. */
+  chosen_srv = get_majority_srv_from_votes(votes, 1);
+  tt_assert(!chosen_srv);
+
+  { /* Now put in 8 more votes. Let SRV_1 have majority. */
+    int i;
+    /* Now 7 votes believe in SRV_1 */
+    for (i = 0; i < 3; i++) {
+      networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
+      smartlist_add(votes, vote);
+    }
+    /* and 2 votes believe in SRV_2 */
+    for (i = 0; i < 2; i++) {
+      networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_2);
+      smartlist_add(votes, vote);
+    }
+    for (i = 0; i < 3; i++) {
+      networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
+      smartlist_add(votes, vote);
+    }
+
+    tt_int_op(smartlist_len(votes), ==, 9);
+  }
+
+  /* Now we achieve majority for SRV_1, but not the AuthDirNumSRVAgreements
+     requirement. So still not picking an SRV. */
+  set_num_srv_agreements(8);
+  chosen_srv = get_majority_srv_from_votes(votes, 1);
+  tt_assert(!chosen_srv);
+
+  /* We will now lower the AuthDirNumSRVAgreements requirement by tweaking the
+   * consensus parameter and we will try again. This time it should work. */
+  set_num_srv_agreements(7);
+  chosen_srv = get_majority_srv_from_votes(votes, 1);
+  tt_assert(chosen_srv);
+  tt_int_op(chosen_srv->num_reveals, ==, 42);
+  tt_mem_op(chosen_srv->value, OP_EQ, SRV_1, sizeof(chosen_srv->value));
+
+ done:
+  SMARTLIST_FOREACH(votes, networkstatus_t *, vote,
+                    networkstatus_vote_free(vote));
+  smartlist_free(votes);
+}
+
+static void
+test_utils(void *arg)
+{
+  (void) arg;
+
+  /* Testing srv_dup(). */
+  {
+    sr_srv_t *srv = NULL, *dup_srv = NULL;
+    const char *srv_value =
+      "1BDB7C3E973936E4D13A49F37C859B3DC69C429334CF9412E3FEF6399C52D47A";
+    srv = tor_malloc_zero(sizeof(*srv));
+    srv->num_reveals = 42;
+    memcpy(srv->value, srv_value, sizeof(srv->value));
+    dup_srv = srv_dup(srv);
+    tt_assert(dup_srv);
+    tt_int_op(dup_srv->num_reveals, ==, srv->num_reveals);
+    tt_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value));
+    tor_free(srv);
+    tor_free(dup_srv);
+  }
+
+  /* Testing commitments_are_the_same(). Currently, the check is to test the
+   * value of the encoded commit so let's make sure that actually works. */
+  {
+    /* Payload of 55 bytes that is the length of
+     * sr_commit_t->encoded_commit. */
+    const char *payload =
+      "\x5d\xb9\x60\xb6\xcc\x51\x68\x52\x31\xd9\x88\x88\x71\x71\xe0\x30"
+      "\x59\x55\x7f\xcd\x61\xc0\x4b\x05\xb8\xcd\xc1\x48\xe9\xcd\x16\x1f"
+      "\x70\x15\x0c\xfc\xd3\x1a\x75\xd0\x93\x6c\xc4\xe0\x5c\xbe\xe2\x18"
+      "\xc7\xaf\x72\xb6\x7c\x9b\x52";
+    sr_commit_t commit1, commit2;
+    memcpy(commit1.encoded_commit, payload, sizeof(commit1.encoded_commit));
+    memcpy(commit2.encoded_commit, payload, sizeof(commit2.encoded_commit));
+    tt_int_op(commitments_are_the_same(&commit1, &commit2), ==, 1);
+    /* Let's corrupt one of them. */
+    memset(commit1.encoded_commit, 'A', sizeof(commit1.encoded_commit));
+    tt_int_op(commitments_are_the_same(&commit1, &commit2), ==, 0);
+  }
+
+  /* Testing commit_is_authoritative(). */
+  {
+    crypto_pk_t *k = crypto_pk_new();
+    char digest[DIGEST_LEN];
+    sr_commit_t commit;
+
+    tt_assert(!crypto_pk_generate_key(k));
+
+    tt_int_op(0, ==, crypto_pk_get_digest(k, digest));
+    memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity));
+    tt_int_op(commit_is_authoritative(&commit, digest), ==, 1);
+    /* Change the pubkey. */
+    memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity));
+    tt_int_op(commit_is_authoritative(&commit, digest), ==, 0);
+  }
+
+  /* Testing get_phase_str(). */
+  {
+    tt_str_op(get_phase_str(SR_PHASE_REVEAL), ==, "reveal");
+    tt_str_op(get_phase_str(SR_PHASE_COMMIT), ==, "commit");
+  }
+
+  /* Testing phase transition */
+  {
+    init_authority_state();
+    set_sr_phase(SR_PHASE_COMMIT);
+    tt_int_op(is_phase_transition(SR_PHASE_REVEAL), ==, 1);
+    tt_int_op(is_phase_transition(SR_PHASE_COMMIT), ==, 0);
+    set_sr_phase(SR_PHASE_REVEAL);
+    tt_int_op(is_phase_transition(SR_PHASE_REVEAL), ==, 0);
+    tt_int_op(is_phase_transition(SR_PHASE_COMMIT), ==, 1);
+    /* Junk. */
+    tt_int_op(is_phase_transition(42), ==, 1);
+  }
+
+ done:
+  return;
+}
+
+static void
+test_state_transition(void *arg)
+{
+  sr_state_t *state = NULL;
+  time_t now = time(NULL);
+
+  (void) arg;
+
+  {  /* Setup a minimal dirauth environment for this test  */
+    init_authority_state();
+    state = get_sr_state();
+    tt_assert(state);
+  }
+
+  /* Test our state reset for a new protocol run. */
+  {
+    /* Add a commit to the state so we can test if the reset cleans the
+     * commits. Also, change all params that we expect to be updated. */
+    sr_commit_t *commit = sr_generate_our_commit(now, mock_cert);
+    tt_assert(commit);
+    sr_state_add_commit(commit);
+    tt_int_op(digestmap_size(state->commits), ==, 1);
+    /* Let's test our delete feature. */
+    sr_state_delete_commits();
+    tt_int_op(digestmap_size(state->commits), ==, 0);
+    /* Add it back so we can continue the rest of the test because after
+     * deletiong our commit will be freed so generate a new one. */
+    commit = sr_generate_our_commit(now, mock_cert);
+    tt_assert(commit);
+    sr_state_add_commit(commit);
+    tt_int_op(digestmap_size(state->commits), ==, 1);
+    state->n_reveal_rounds = 42;
+    state->n_commit_rounds = 43;
+    state->n_protocol_runs = 44;
+    reset_state_for_new_protocol_run(now);
+    tt_int_op(state->n_reveal_rounds, ==, 0);
+    tt_int_op(state->n_commit_rounds, ==, 0);
+    tt_u64_op(state->n_protocol_runs, ==, 45);
+    tt_int_op(digestmap_size(state->commits), ==, 0);
+  }
+
+  /* Test SRV rotation in our state. */
+  {
+    const sr_srv_t *cur, *prev;
+    test_sr_setup_srv(1);
+    cur = sr_state_get_current_srv();
+    tt_assert(cur);
+    /* After, current srv should be the previous and then set to NULL. */
+    state_rotate_srv();
+    prev = sr_state_get_previous_srv();
+    tt_assert(prev == cur);
+    tt_assert(!sr_state_get_current_srv());
+  }
+
+  /* New protocol run. */
+  {
+    const sr_srv_t *cur;
+    /* Setup some new SRVs so we can confirm that a new protocol run
+     * actually makes them rotate and compute new ones. */
+    test_sr_setup_srv(1);
+    cur = sr_state_get_current_srv();
+    tt_assert(cur);
+    set_sr_phase(SR_PHASE_REVEAL);
+    MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+    new_protocol_run(now);
+    UNMOCK(get_my_v3_authority_cert);
+    /* Rotation happened. */
+    tt_assert(sr_state_get_previous_srv() == cur);
+    /* We are going into COMMIT phase so we had to rotate our SRVs. Usually
+     * our current SRV would be NULL but a new protocol run should make us
+     * compute a new SRV. */
+    tt_assert(sr_state_get_current_srv());
+    /* Also, make sure we did change the current. */
+    tt_assert(sr_state_get_current_srv() != cur);
+    /* We should have our commitment alone. */
+    tt_int_op(digestmap_size(state->commits), ==, 1);
+    tt_int_op(state->n_reveal_rounds, ==, 0);
+    tt_int_op(state->n_commit_rounds, ==, 0);
+    /* 46 here since we were at 45 just before. */
+    tt_u64_op(state->n_protocol_runs, ==, 46);
+  }
+
+  /* Cleanup of SRVs. */
+  {
+    sr_state_clean_srvs();
+    tt_assert(!sr_state_get_current_srv());
+    tt_assert(!sr_state_get_previous_srv());
+  }
+
+ done:
+  return;
+}
+
+static void
+test_keep_commit(void *arg)
+{
+  char fp[FINGERPRINT_LEN + 1];
+  sr_commit_t *commit = NULL, *dup_commit = NULL;
+  sr_state_t *state;
+  time_t now = time(NULL);
+
+  (void) arg;
+
+  MOCK(trusteddirserver_get_by_v3_auth_digest,
+       trusteddirserver_get_by_v3_auth_digest_m);
+
+  {  /* Setup a minimal dirauth environment for this test  */
+    crypto_pk_t *k = crypto_pk_new();
+    /* Have a key that is not the one from our commit. */
+    tt_int_op(0, ==, crypto_pk_generate_key(k));
+    tt_int_op(0, ==, crypto_pk_get_fingerprint(k, fp, 0));
+    init_authority_state();
+    state = get_sr_state();
+  }
+
+  /* Test this very important function that tells us if we should keep a
+   * commit or not in our state. Most of it depends on the phase and what's
+   * in the commit so we'll change the commit as we go. */
+  commit = sr_generate_our_commit(now, mock_cert);
+  tt_assert(commit);
+  /* Set us in COMMIT phase for starter. */
+  set_sr_phase(SR_PHASE_COMMIT);
+  /* We should never keep a commit from a non authoritative authority. */
+  tt_int_op(should_keep_commit(commit, fp, SR_PHASE_COMMIT), ==, 0);
+  /* This should NOT be kept because it has a reveal value in it. */
+  tt_assert(commit_has_reveal_value(commit));
+  tt_int_op(should_keep_commit(commit, commit->rsa_identity,
+                               SR_PHASE_COMMIT), ==, 0);
+  /* Add it to the state which should return to not keep it. */
+  sr_state_add_commit(commit);
+  tt_int_op(should_keep_commit(commit, commit->rsa_identity,
+                               SR_PHASE_COMMIT), ==, 0);
+  /* Remove it from state so we can continue our testing. */
+  digestmap_remove(state->commits, commit->rsa_identity);
+  /* Let's remove our reveal value which should make it OK to keep it. */
+  memset(commit->encoded_reveal, 0, sizeof(commit->encoded_reveal));
+  tt_int_op(should_keep_commit(commit, commit->rsa_identity,
+                               SR_PHASE_COMMIT), ==, 1);
+
+  /* Let's reset our commit and go into REVEAL phase. */
+  sr_commit_free(commit);
+  commit = sr_generate_our_commit(now, mock_cert);
+  tt_assert(commit);
+  /* Dup the commit so we have one with and one without a reveal value. */
+  dup_commit = tor_malloc_zero(sizeof(*dup_commit));
+  memcpy(dup_commit, commit, sizeof(*dup_commit));
+  memset(dup_commit->encoded_reveal, 0, sizeof(dup_commit->encoded_reveal));
+  set_sr_phase(SR_PHASE_REVEAL);
+  /* We should never keep a commit from a non authoritative authority. */
+  tt_int_op(should_keep_commit(commit, fp, SR_PHASE_REVEAL), ==, 0);
+  /* We shouldn't accept a commit that is not in our state. */
+  tt_int_op(should_keep_commit(commit, commit->rsa_identity,
+                               SR_PHASE_REVEAL), ==, 0);
+  /* Important to add the commit _without_ the reveal here. */
+  sr_state_add_commit(dup_commit);
+  tt_int_op(digestmap_size(state->commits), ==, 1);
+  /* Our commit should be valid that is authoritative, contains a reveal, be
+   * in the state and commitment and reveal values match. */
+  tt_int_op(should_keep_commit(commit, commit->rsa_identity,
+                               SR_PHASE_REVEAL), ==, 1);
+  /* The commit shouldn't be kept if it's not verified that is no matchin
+   * hashed reveal. */
+  {
+    /* Let's save the hash reveal so we can restore it. */
+    sr_commit_t place_holder;
+    memcpy(place_holder.hashed_reveal, commit->hashed_reveal,
+           sizeof(place_holder.hashed_reveal));
+    memset(commit->hashed_reveal, 0, sizeof(commit->hashed_reveal));
+    tt_int_op(should_keep_commit(commit, commit->rsa_identity,
+                                 SR_PHASE_REVEAL), ==, 0);
+    memcpy(commit->hashed_reveal, place_holder.hashed_reveal,
+           sizeof(commit->hashed_reveal));
+  }
+  /* We shouldn't keep a commit that has no reveal. */
+  tt_int_op(should_keep_commit(dup_commit, dup_commit->rsa_identity,
+                               SR_PHASE_REVEAL), ==, 0);
+  /* We must not keep a commit that is not the same from the commit phase. */
+  memset(commit->encoded_commit, 0, sizeof(commit->encoded_commit));
+  tt_int_op(should_keep_commit(commit, commit->rsa_identity,
+                               SR_PHASE_REVEAL), ==, 0);
+
+ done:
+  sr_commit_free(commit);
+  sr_commit_free(dup_commit);
+  UNMOCK(trusteddirserver_get_by_v3_auth_digest);
+}
+
+static void
+test_state_update(void *arg)
+{
+  time_t commit_phase_time = 1452076000;
+  time_t reveal_phase_time = 1452086800;
+  sr_state_t *state;
+
+  (void) arg;
+
+  {
+    init_authority_state();
+    state = get_sr_state();
+    set_sr_phase(SR_PHASE_COMMIT);
+    /* We'll cheat a bit here and reset the creation time of the state which
+     * will avoid us to compute a valid_after time that fits the commit
+     * phase. */
+    state->valid_after = 0;
+    state->n_reveal_rounds = 0;
+    state->n_commit_rounds = 0;
+  }
+
+  /* We need to mock for the state update function call. */
+  MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+
+  /* We are in COMMIT phase here and we'll trigger a state update but no
+   * transition. */
+  sr_state_update(commit_phase_time);
+  tt_int_op(state->valid_after, ==, commit_phase_time);
+  tt_int_op(state->n_commit_rounds, ==, 1);
+  tt_int_op(state->phase, ==, SR_PHASE_COMMIT);
+  tt_int_op(digestmap_size(state->commits), ==, 1);
+
+  /* We are still in the COMMIT phase here but we'll trigger a state
+   * transition to the REVEAL phase. */
+  sr_state_update(reveal_phase_time);
+  tt_int_op(state->phase, ==, SR_PHASE_REVEAL);
+  tt_int_op(state->valid_after, ==, reveal_phase_time);
+  /* Only our commit should be in there. */
+  tt_int_op(digestmap_size(state->commits), ==, 1);
+  tt_int_op(state->n_reveal_rounds, ==, 1);
+
+  /* We can't update a state with a valid after _lower_ than the creation
+   * time so here it is. */
+  sr_state_update(commit_phase_time);
+  tt_int_op(state->valid_after, ==, reveal_phase_time);
+
+  /* Finally, let's go back in COMMIT phase so we can test the state update
+   * of a new protocol run. */
+  state->valid_after = 0;
+  sr_state_update(commit_phase_time);
+  tt_int_op(state->valid_after, ==, commit_phase_time);
+  tt_int_op(state->n_commit_rounds, ==, 1);
+  tt_int_op(state->n_reveal_rounds, ==, 0);
+  tt_u64_op(state->n_protocol_runs, ==, 1);
+  tt_int_op(state->phase, ==, SR_PHASE_COMMIT);
+  tt_int_op(digestmap_size(state->commits), ==, 1);
+  tt_assert(state->current_srv);
+
+ done:
+  sr_state_free();
+  UNMOCK(get_my_v3_authority_cert);
+}
+
+struct testcase_t sr_tests[] = {
+  { "get_sr_protocol_phase", test_get_sr_protocol_phase, TT_FORK,
+    NULL, NULL },
+  { "sr_commit", test_sr_commit, TT_FORK,
+    NULL, NULL },
+  { "keep_commit", test_keep_commit, TT_FORK,
+    NULL, NULL },
+  { "encoding", test_encoding, TT_FORK,
+    NULL, NULL },
+  { "get_next_valid_after_time", test_get_next_valid_after_time, TT_FORK,
+    NULL, NULL },
+  { "get_state_valid_until_time", test_get_state_valid_until_time, TT_FORK,
+    NULL, NULL },
+  { "vote", test_vote, TT_FORK,
+    NULL, NULL },
+  { "state_load_from_disk", test_state_load_from_disk, TT_FORK,
+    NULL, NULL },
+  { "sr_compute_srv", test_sr_compute_srv, TT_FORK, NULL, NULL },
+  { "sr_get_majority_srv_from_votes", test_sr_get_majority_srv_from_votes,
+    TT_FORK, NULL, NULL },
+  { "utils", test_utils, TT_FORK, NULL, NULL },
+  { "state_transition", test_state_transition, TT_FORK, NULL, NULL },
+  { "state_update", test_state_update, TT_FORK,
+    NULL, NULL },
+  END_OF_TESTCASES
+};