tor-commits
Threads by month
- ----- 2025 -----
- May
- April
- March
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
July 2016
- 21 participants
- 1271 discussions
commit 9744a40f7aa71f874b9f3eeb3c5e7d0899c8d409
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Mon Jan 11 11:13:04 2016 +0100
Add tor_htonll/ntohll functions
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/common/util.c | 21 +++++++++++++++++++++
src/common/util.h | 2 ++
2 files changed, 23 insertions(+)
diff --git a/src/common/util.c b/src/common/util.c
index 4b6df81..3c5341d 100644
--- a/src/common/util.c
+++ b/src/common/util.c
@@ -5587,3 +5587,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);
+}
+
diff --git a/src/common/util.h b/src/common/util.h
index 7cb33dc..fdf1c03 100644
--- a/src/common/util.h
+++ b/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

01 Jul '16
commit b3b4ffce2e23bfb6a2af374cd8bfa5981628a342
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 3 10:18:45 2016 -0400
prop250: Add memory and disk state in new files
This commit introduces two new files with their header.
"shared_random.c" contains basic functions to initialize the state and allow
commit decoding for the disk state to be able to parse them from disk.
"shared_random_state.c" contains everything that has to do with the state
for both our memory and disk. Lots of helper functions as well as a
mechanism to query the state in a synchronized way.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
Signed-off-by: George Kadianakis <desnacked(a)riseup.net>
---
src/or/dirvote.c | 90 ++--
src/or/dirvote.h | 32 ++
src/or/include.am | 4 +
src/or/shared_random.c | 286 +++++++++++
src/or/shared_random.h | 114 +++++
src/or/shared_random_state.c | 1086 ++++++++++++++++++++++++++++++++++++++++++
src/or/shared_random_state.h | 126 +++++
7 files changed, 1699 insertions(+), 39 deletions(-)
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index ad0696e..a08f6ec 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -2511,50 +2511,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 = {0};
/** 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 +2580,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 +2588,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>. */
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index 0b1d284..f2080a5 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -121,12 +121,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,
diff --git a/src/or/include.am b/src/or/include.am
index 19f1a7f..744a507 100644
--- a/src/or/include.am
+++ b/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 \
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
new file mode 100644
index 0000000..447ab27
--- /dev/null
+++ b/src/or/shared_random.c
@@ -0,0 +1,286 @@
+/* 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.
+ **/
+
+#define SHARED_RANDOM_PRIVATE
+
+#include "or.h"
+#include "shared_random.h"
+#include "config.h"
+#include "confparse.h"
+#include "networkstatus.h"
+#include "routerkeys.h"
+#include "router.h"
+#include "routerlist.h"
+#include "shared_random_state.h"
+
+/* Allocate a new commit object and initializing it with <b>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_fpr)
+{
+ sr_commit_t *commit;
+
+ tor_assert(rsa_identity_fpr);
+
+ commit = tor_malloc_zero(sizeof(*commit));
+ commit->alg = SR_DIGEST_ALG;
+ strlcpy(commit->rsa_identity_fpr, rsa_identity_fpr,
+ sizeof(commit->rsa_identity_fpr));
+ return commit;
+}
+
+/* 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.",
+ commit->rsa_identity_fpr);
+ 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 %d).",
+ commit->rsa_identity_fpr, decoded_len, SR_COMMIT_LEN);
+ goto error;
+ }
+
+ /* First is the timestamp (8 bytes). */
+ commit->commit_ts = (time_t) 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.",
+ commit->rsa_identity_fpr);
+ 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 %d)",
+ commit->rsa_identity_fpr, decoded_len, SR_REVEAL_LEN);
+ goto error;
+ }
+
+ commit->reveal_ts = (time_t) 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;
+}
+
+/* Cleanup both our global state and disk state. */
+static void
+sr_cleanup(void)
+{
+ sr_state_free();
+}
+
+/* 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);
+}
+
+/* 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 num_reveals, ok;
+ 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 = (int)tor_parse_long(smartlist_get(args, 0),
+ 10, 0, INT32_MAX, &ok, NULL);
+ if (!ok) {
+ goto end;
+ }
+ srv = tor_malloc_zero(sizeof(*srv));
+ srv->num_reveals = num_reveals;
+
+ /* Second and last argument is the shared random value it self. */
+ value = smartlist_get(args, 1);
+ base16_decode((char *) srv->value, sizeof(srv->value), value,
+ HEX_DIGEST256_LEN);
+ 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:
+ * algname, RSA fingerprint, commit value[, reveal value]
+ */
+sr_commit_t *
+sr_parse_commit(const smartlist_t *args)
+{
+ char *value;
+ digest_algorithm_t alg;
+ const char *rsa_identity_fpr;
+ sr_commit_t *commit = NULL;
+
+ if (smartlist_len(args) < 3) {
+ goto error;
+ }
+
+ /* First argument is the algorithm. */
+ value = smartlist_get(args, 0);
+ 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;
+ }
+
+ /* Second argument is the RSA fingerprint of the auth */
+ rsa_identity_fpr = smartlist_get(args, 1);
+ if (base16_decode(digest, DIGEST_LEN, rsa_identity_fpr,
+ HEX_DIGEST_LEN) < 0) {
+ log_warn(LD_DIR, "SR: RSA fingerprint '%s' not decodable",
+ rsa_identity_fpr);
+ goto error;
+ }
+ /* 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(digest) == NULL) {
+ log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized "
+ "authority. Discarding commit.",
+ rsa_identity_fpr);
+ goto error;
+ }
+
+ /* Allocate commit since we have a valid identity now. */
+ commit = commit_new(rsa_identity_fpr);
+
+ /* Third argument is the commitment value base64-encoded. */
+ value = smartlist_get(args, 2);
+ if (commit_decode(value, commit) < 0) {
+ goto error;
+ }
+
+ /* (Optional) Fourth argument is the revealed value. */
+ if (smartlist_len(args) > 3) {
+ value = smartlist_get(args, 3);
+ if (reveal_decode(value, commit) < 0) {
+ goto error;
+ }
+ }
+
+ return commit;
+
+ error:
+ sr_commit_free(commit);
+ return NULL;
+}
+
+/* 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();
+}
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
new file mode 100644
index 0000000..447de0c
--- /dev/null
+++ b/src/or/shared_random.h
@@ -0,0 +1,114 @@
+/* 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_8(version) | PREV_SRV */
+#define SR_SRV_MSG_LEN \
+ (SR_SRV_TOKEN_LEN + 1 + 1 + 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)
+
+/* 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. */
+ int 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;
+
+ /* Commit owner info */
+
+ /* The RSA identity fingerprint of the authority. */
+ char rsa_identity_fpr[FINGERPRINT_LEN + 1];
+
+ /* Commitment information */
+
+ /* Timestamp of reveal. Correspond to TIMESTAMP. */
+ time_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. */
+ time_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_commit_free(sr_commit_t *commit);
+
+/* Private methods (only used by shared_random_state.c): */
+
+sr_commit_t *sr_parse_commit(const smartlist_t *args);
+sr_srv_t *sr_parse_srv(const smartlist_t *args);
+
+#ifdef SHARED_RANDOM_PRIVATE
+
+/* Decode. */
+STATIC int commit_decode(const char *encoded, sr_commit_t *commit);
+STATIC int reveal_decode(const char *encoded, sr_commit_t *commit);
+
+#endif /* SHARED_RANDOM_PRIVATE */
+
+#endif /* TOR_SHARED_RANDOM_H */
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
new file mode 100644
index 0000000..9fd3b27
--- /dev/null
+++ b/src/or/shared_random_state.c
@@ -0,0 +1,1086 @@
+/* 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";
+
+/* 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, INT, "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 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_fpr,
+ 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'", commit->rsa_identity_fpr,
+ 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;
+ }
+ /* 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, "%s %s %s%s",
+ crypto_digest_algorithm_get_name(commit->alg),
+ commit->rsa_identity_fpr,
+ 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[HEX_DIGEST256_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;
+ }
+ base16_encode(encoded, sizeof(encoded), (const char *) srv->value,
+ sizeof(srv->value));
+ tor_asprintf(&line->value, "%d %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_notice(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;
+}
+
+/* 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 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);
+ }
+}
+
+/* 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_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();
+ }
+}
+
+/* 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. */
+sr_srv_t *
+sr_state_get_previous_srv(void)
+{
+ 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. */
+sr_srv_t *
+sr_state_get_current_srv(void)
+{
+ 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);
+}
+
+/* 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;
+}
+
+/* Return commit object from the given authority digest <b>identity</b>.
+ * Return NULL if not found. */
+sr_commit_t *
+sr_state_get_commit(const char *rsa_fpr)
+{
+ sr_commit_t *commit;
+
+ tor_assert(rsa_fpr);
+
+ state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_COMMIT,
+ (void *) rsa_fpr, (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.",
+ commit->rsa_identity_fpr);
+}
+
+/* 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);
+}
+
+/* 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);
+}
+
+/* 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);
+ }
+ }
+ return 0;
+
+ error:
+ return -1;
+}
diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h
new file mode 100644
index 0000000..a9a80ee
--- /dev/null
+++ b/src/or/shared_random_state.h
@@ -0,0 +1,126 @@
+/* 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_ALL = 3,
+ SR_STATE_ACTION_SAVE = 4,
+} 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. */
+ uint8_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. */
+ int 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: */
+
+/* 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);
+sr_srv_t *sr_state_get_previous_srv(void);
+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);
+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);
+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);
+
+#endif /* SHARED_RANDOM_STATE_PRIVATE */
+
+#endif /* TOR_SHARED_RANDOM_STATE_H */
1
0

01 Jul '16
commit 5b183328fdd4bf81a3a38afe88d2766e61773fb9
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 3 10:57:49 2016 -0400
prop250: Add commit and SR values generation code
This adds the logic of commit and SR values generation. Furthermore, the
concept of a protocol run is added that is commit is generated at the right
time as well as SR values which are also rotated before a new protocol run.
Signed-off-by: George Kadianakis <desnacked(a)riseup.net>
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 315 ++++++++++++++++++++++++++++++++++++++++++-
src/or/shared_random.h | 14 ++
src/or/shared_random_state.c | 186 ++++++++++++++++++++++++-
src/or/shared_random_state.h | 11 ++
4 files changed, 516 insertions(+), 10 deletions(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 447ab27..dd567bc 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -37,6 +37,35 @@ commit_new(const char *rsa_identity_fpr)
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", commit->rsa_identity_fpr);
+
+ if (commit->commit_ts >= 0) {
+ log_debug(LD_DIR, "SR: Commit: [TS: %ld] [Encoded: %s]",
+ commit->commit_ts, commit->encoded_commit);
+ }
+
+ if (commit->reveal_ts >= 0) {
+ log_debug(LD_DIR, "SR: Reveal: [TS: %ld] [Encoded: %s]",
+ commit->reveal_ts, safe_str(commit->encoded_reveal));
+ } else {
+ log_debug(LD_DIR, "SR: Reveal: UNKNOWN");
+ }
+}
+
+/* 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) )
*
@@ -144,6 +173,62 @@ reveal_decode(const char *encoded, sr_commit_t *commit)
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((uint64_t) 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)
@@ -151,6 +236,96 @@ 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", commit->rsa_identity_fpr,
+ 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_8(version) | HASHED_REVEALS | previous_SRV)
+ * This function cannot fail. */
+static sr_srv_t *
+generate_srv(const char *hashed_reveals, uint8_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_uint8(msg + offset, reveal_num);
+ offset += 1;
+ set_uint8(msg + offset, SR_PROTO_VERSION);
+ offset += 1;
+ 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, srv);
+ log_debug(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));
+}
+
+/* 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, 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);
+
+ 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));
+ strlcpy(dst, buf, sizeof(buf));
+}
+
/* Free a commit object. */
void
sr_commit_free(sr_commit_t *commit)
@@ -163,6 +338,123 @@ sr_commit_free(sr_commit_t *commit)
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 fingerprint[FINGERPRINT_LEN+1];
+
+ tor_assert(my_rsa_cert);
+
+ /* Get our RSA identity fingerprint */
+ if (crypto_pk_get_fingerprint(my_rsa_cert->identity_key,
+ fingerprint, 0) < 0) {
+ goto error;
+ }
+
+ /* New commit with our identity key. */
+ commit = commit_new(fingerprint);
+
+ /* 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);
+ 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)
+{
+ size_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) {
+ 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;
+ }
+ tor_assert(reveal_num < UINT8_MAX);
+ current_srv = generate_srv(hashed_reveals, (uint8_t) 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.
@@ -174,7 +466,7 @@ sr_srv_t *
sr_parse_srv(const smartlist_t *args)
{
char *value;
- int num_reveals, ok;
+ int num_reveals, ok, ret;
sr_srv_t *srv = NULL;
tor_assert(args);
@@ -189,13 +481,24 @@ sr_parse_srv(const smartlist_t *args)
if (!ok) {
goto end;
}
- srv = tor_malloc_zero(sizeof(*srv));
- srv->num_reveals = num_reveals;
-
/* Second and last argument is the shared random value it self. */
value = smartlist_get(args, 1);
- base16_decode((char *) srv->value, sizeof(srv->value), value,
- HEX_DIGEST256_LEN);
+ 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;
}
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index 447de0c..878bfbf 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -42,6 +42,11 @@
* 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)
/* Protocol phase. */
typedef enum {
@@ -97,18 +102,27 @@ typedef struct sr_commit_t {
int sr_init(int save_to_disk);
void sr_save_and_cleanup(void);
void sr_commit_free(sr_commit_t *commit);
+void sr_srv_encode(char *dst, const sr_srv_t *srv);
/* Private methods (only used by shared_random_state.c): */
sr_commit_t *sr_parse_commit(const smartlist_t *args);
sr_srv_t *sr_parse_srv(const smartlist_t *args);
+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);
+
#endif /* SHARED_RANDOM_PRIVATE */
#endif /* TOR_SHARED_RANDOM_H */
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 9fd3b27..87bdfc0 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -22,6 +22,9 @@
/* 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. */
@@ -88,6 +91,25 @@ static const config_format_t state_format = {
&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)
@@ -554,7 +576,7 @@ disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line)
static void
disk_state_put_srv_line(const sr_srv_t *srv, config_line_t *line)
{
- char encoded[HEX_DIGEST256_LEN + 1];
+ char encoded[SR_SRV_VALUE_BASE64_LEN + 1];
tor_assert(line);
@@ -563,8 +585,7 @@ disk_state_put_srv_line(const sr_srv_t *srv, config_line_t *line)
if (srv == NULL) {
return;
}
- base16_encode(encoded, sizeof(encoded), (const char *) srv->value,
- sizeof(srv->value));
+ sr_srv_encode(encoded, srv);
tor_asprintf(&line->value, "%d %s", srv->num_reveals, encoded);
}
@@ -748,6 +769,82 @@ disk_state_save_to_disk(void)
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();
+}
+
+/* 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)
+{
+ /* Get a pointer to the previous SRV so we can free it after rotation. */
+ sr_srv_t *previous_srv = sr_state_get_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);
+ tor_free(previous_srv);
+}
+
+/* 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 *
@@ -756,7 +853,6 @@ 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 *
@@ -941,6 +1037,20 @@ sr_state_set_current_srv(const sr_srv_t *srv)
NULL);
}
+/* Clean all the SRVs in our state. */
+void
+sr_state_clean_srvs(void)
+{
+ sr_srv_t *previous_srv = sr_state_get_previous_srv();
+ sr_srv_t *current_srv = sr_state_get_current_srv();
+
+ tor_free(previous_srv);
+ sr_state_set_previous_srv(NULL);
+
+ tor_free(current_srv);
+ sr_state_set_current_srv(NULL);
+}
+
/* Return a pointer to the commits map from our state. CANNOT be NULL. */
digestmap_t *
sr_state_get_commits(void)
@@ -952,6 +1062,68 @@ sr_state_get_commits(void)
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 new voting period (%s). "
+ "Current phase is %s (%d/%d).",
+ 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>identity</b>.
* Return NULL if not found. */
sr_commit_t *
@@ -1079,6 +1251,12 @@ sr_state_init(int save_to_disk, int read_from_disk)
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:
diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h
index a9a80ee..d9d9751 100644
--- a/src/or/shared_random_state.h
+++ b/src/or/shared_random_state.h
@@ -95,6 +95,8 @@ typedef struct sr_disk_state_t {
/* 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);
@@ -108,6 +110,8 @@ 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);
@@ -118,8 +122,15 @@ 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 */
1
0
commit 63ca307127f70f2df3da20d50ecc545939658991
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 17 15:05:32 2016 -0400
prop250: Improve log messages
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 6 +++---
src/or/shared_random_state.c | 2 +-
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index c5a62a7..15c33af 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -1102,8 +1102,8 @@ sr_parse_commit(const smartlist_t *args)
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",
- rsa_identity_fpr);
+ log_warn(LD_DIR, "SR: RSA fingerprint %s not decodable",
+ escaped(rsa_identity_fpr));
goto error;
}
/* Let's make sure, for extra safety, that this fingerprint is known to
@@ -1112,7 +1112,7 @@ sr_parse_commit(const smartlist_t *args)
if (trusteddirserver_get_by_v3_auth_digest(digest) == NULL) {
log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized "
"authority. Discarding commit.",
- rsa_identity_fpr);
+ escaped(rsa_identity_fpr));
goto error;
}
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 9700bf5..cce0c99 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -1122,7 +1122,7 @@ sr_state_update(time_t valid_after)
char tbuf[ISO_TIME_LEN + 1];
format_iso_time(tbuf, valid_after);
log_info(LD_DIR, "SR: State prepared for new voting period (%s). "
- "Current phase is %s (%d/%d).",
+ "Current phase is %s (%d commit & %d reveal rounds).",
tbuf, get_phase_str(sr_state->phase),
sr_state->n_commit_rounds, sr_state->n_reveal_rounds);
}
1
0

[tor/master] prop250: Only trust known authority when computing SRV
by nickm@torproject.org 01 Jul '16
by nickm@torproject.org 01 Jul '16
01 Jul '16
commit 545b77e2f8fac6cf5909bb6b69b52d9cb4f8f397
Author: David Goulet <dgoulet(a)torproject.org>
Date: Wed May 11 16:02:18 2016 -0400
prop250: Only trust known authority when computing SRV
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 6e6ff3b..d409d10 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -951,6 +951,16 @@ sr_compute_srv(void)
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_);
1
0
commit c0cec2ffd3d5f965413f56e93f89fabf81a2c3a0
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Mon May 9 16:51:32 2016 -0400
prop250: change time_t to uint64_t
Signed-off-by: David Goulet <dgoulet(a)ev0ke.net>
---
src/or/shared_random.c | 27 ++++++++++-----------------
src/or/shared_random.h | 4 ++--
2 files changed, 12 insertions(+), 19 deletions(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index e3fe1a9..f5f4ccf 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -144,18 +144,10 @@ commit_log(const sr_commit_t *commit)
tor_assert(commit);
log_debug(LD_DIR, "SR: Commit from %s", commit->rsa_identity_fpr);
-
- if (commit->commit_ts >= 0) {
- log_debug(LD_DIR, "SR: Commit: [TS: %ld] [Encoded: %s]",
- commit->commit_ts, commit->encoded_commit);
- }
-
- if (commit->reveal_ts >= 0) {
- log_debug(LD_DIR, "SR: Reveal: [TS: %ld] [Encoded: %s]",
- commit->reveal_ts, safe_str(commit->encoded_reveal));
- } else {
- log_debug(LD_DIR, "SR: Reveal: UNKNOWN");
- }
+ 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>
@@ -172,8 +164,9 @@ verify_commit_and_reveal(const sr_commit_t *commit)
/* Check that the timestamps match. */
if (commit->commit_ts != commit->reveal_ts) {
- log_warn(LD_BUG, "SR: Commit timestamp %ld doesn't match reveal "
- "timestamp %ld", 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;
}
@@ -261,7 +254,7 @@ commit_decode(const char *encoded, sr_commit_t *commit)
}
/* First is the timestamp (8 bytes). */
- commit->commit_ts = (time_t) tor_ntohll(get_uint64(b64_decoded));
+ commit->commit_ts = tor_ntohll(get_uint64(b64_decoded));
offset += sizeof(uint64_t);
/* Next is hashed reveal. */
memcpy(commit->hashed_reveal, b64_decoded + offset,
@@ -313,7 +306,7 @@ reveal_decode(const char *encoded, sr_commit_t *commit)
goto error;
}
- commit->reveal_ts = (time_t) tor_ntohll(get_uint64(b64_decoded));
+ 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));
@@ -371,7 +364,7 @@ commit_encode(const sr_commit_t *commit, char *dst, size_t len)
tor_assert(dst);
/* First is the timestamp (8 bytes). */
- set_uint64(buf, tor_htonll((uint64_t) commit->commit_ts));
+ set_uint64(buf, tor_htonll(commit->commit_ts));
offset += sizeof(uint64_t);
/* and then the hashed reveal. */
memcpy(buf + offset, commit->hashed_reveal,
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index d52258c..15eed8a 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -77,7 +77,7 @@ typedef struct sr_commit_t {
/* Commitment information */
/* Timestamp of reveal. Correspond to TIMESTAMP. */
- time_t reveal_ts;
+ 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. */
@@ -90,7 +90,7 @@ typedef struct sr_commit_t {
* avoiding possible information leaks of our PRNG. */
uint8_t random_number[SR_RANDOM_NUMBER_LEN];
/* Timestamp of commit. Correspond to TIMESTAMP. */
- time_t commit_ts;
+ 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;
1
0

[tor/master] prop250: Sort commits in lexicographical order in votes
by nickm@torproject.org 01 Jul '16
by nickm@torproject.org 01 Jul '16
01 Jul '16
commit cd858d78a7d04fa64b9af3d602c435154acd284f
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 17 15:13:13 2016 -0400
prop250: Sort commits in lexicographical order in votes
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 15c33af..23c2b14 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -1205,11 +1205,21 @@ sr_get_string_for_vote(void)
/* 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(chunks, line);
+ 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(),
1
0
commit ebbff3174059f0c1d0fb51a39f97de453ea055f4
Author: George Kadianakis <desnacked(a)riseup.net>
Date: Tue May 24 13:58:20 2016 +0300
prop250: Silence a logging message.
LOG_NOTICE is a bit too much for that one.
---
src/or/shared_random.c | 12 ++++++++++--
src/or/shared_random_state.c | 6 +++---
2 files changed, 13 insertions(+), 5 deletions(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 7da6daf..599273f 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -438,7 +438,7 @@ generate_srv(const char *hashed_reveals, uint8_t 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_debug(LD_DIR, "SR: Generated SRV: %s", srv_hash_encoded);
+ log_info(LD_DIR, "SR: Generated SRV: %s", srv_hash_encoded);
}
return srv;
}
@@ -617,7 +617,15 @@ should_keep_commit(const sr_commit_t *commit, const char *voter_key,
case SR_PHASE_COMMIT:
/* Already having a commit for an authority so ignore this one. */
if (saved_commit) {
- log_debug(LD_DIR, "SR: Ignoring known commit during COMMIT phase.");
+ /* 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;
}
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 705c586..87126f5 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -720,7 +720,7 @@ disk_state_load_from_disk_impl(const char *fname)
state_set(parsed_state);
disk_state_set(disk_state);
tor_free(content);
- log_notice(LD_DIR, "SR: State loaded successfully from file %s", fname);
+ log_info(LD_DIR, "SR: State loaded successfully from file %s", fname);
return 0;
error:
@@ -1121,8 +1121,8 @@ sr_state_update(time_t valid_after)
{ /* Debugging. */
char tbuf[ISO_TIME_LEN + 1];
format_iso_time(tbuf, valid_after);
- log_info(LD_DIR, "SR: State prepared for new voting period (%s). "
- "Current phase is %s (%d commit & %d reveal rounds).",
+ 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);
}
1
0

[tor/master] prop250: Sort smartlist before you get most frequent SRV.
by nickm@torproject.org 01 Jul '16
by nickm@torproject.org 01 Jul '16
01 Jul '16
commit edea0441477cf7f1293ce848abf59b3f1033b28a
Author: George Kadianakis <desnacked(a)riseup.net>
Date: Fri May 13 13:42:33 2016 -0400
prop250: Sort smartlist before you get most frequent SRV.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 10 ++++++++++
src/test/test_shared_random.c | 6 +++++-
2 files changed, 15 insertions(+), 1 deletion(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index d409d10..c5a62a7 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -779,6 +779,15 @@ 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
@@ -813,6 +822,7 @@ get_majority_srv_from_votes(const smartlist_t *votes, int current)
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;
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index fbb4414..5a9c123 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -844,7 +844,7 @@ test_sr_get_majority_srv_from_votes(void *arg)
{ /* Now put in 8 more votes. Let SRV_1 have majority. */
int i;
/* Now 7 votes believe in SRV_1 */
- for (i = 0; i < 6; i++) {
+ for (i = 0; i < 3; i++) {
networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
smartlist_add(votes, vote);
}
@@ -853,6 +853,10 @@ test_sr_get_majority_srv_from_votes(void *arg)
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);
}
1
0
commit 8ac88f6f9739c5fd5fd5b47af63592faa80b8307
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 10 11:37:28 2016 -0400
prop250: Add a valid flag to sr_commit_t
We assert on it using the ASSERT_COMMIT_VALID() macro in critical places
where we use them expecting a commit to be valid.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 9 +++++++++
src/or/shared_random.h | 5 +++++
src/or/shared_random_state.c | 3 +++
3 files changed, 17 insertions(+)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 967e1e1..e56a239 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -700,6 +700,8 @@ 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 */
@@ -914,6 +916,8 @@ sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert)
log_debug(LD_DIR, "SR: Generated our commitment:");
commit_log(commit);
+ /* Our commit better be valid :). */
+ commit->valid = 1;
return commit;
error:
@@ -942,6 +946,8 @@ sr_compute_srv(void)
/* 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);
smartlist_add(commits, c);
} DIGESTMAP_FOREACH_END;
smartlist_sort(commits, compare_reveal_);
@@ -1130,6 +1136,9 @@ sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key)
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);
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index 573c499..f89f47a 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -48,6 +48,9 @@
#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 */
@@ -68,6 +71,8 @@ typedef struct sr_srv_t {
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 */
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 326b8c9..115d954 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -410,6 +410,9 @@ disk_state_parse_commits(sr_state_t *state,
* 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);
1
0