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

[tor/master] prop250: Fix unit tests about the RSA fingerprint check
by nickm@torproject.org 01 Jul '16
by nickm@torproject.org 01 Jul '16
01 Jul '16
commit d43646e191dec6f800f63acb6a6861190b77d86f
Author: David Goulet <dgoulet(a)torproject.org>
Date: Thu May 12 13:30:07 2016 -0400
prop250: Fix unit tests about the RSA fingerprint check
Code has been changed so every RSA fingerprint for a commit in our state is
validated before being used. This fixes the unit tests by mocking one of the
key function and updating the hardcoded state string.
Also, fix a time parsing overflow on platforms with 32bit time_t
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
Signed-off-by: George Kadianakis <desnacked(a)riseup.net>
---
src/or/routerlist.c | 4 +--
src/or/routerlist.h | 3 +-
src/test/test_shared_random.c | 70 +++++++++++++++++++++++++++++++++----------
3 files changed, 58 insertions(+), 19 deletions(-)
diff --git a/src/or/routerlist.c b/src/or/routerlist.c
index bdb3d49..4d55b2e 100644
--- a/src/or/routerlist.c
+++ b/src/or/routerlist.c
@@ -1494,8 +1494,8 @@ router_digest_is_fallback_dir(const char *digest)
* v3 identity key hashes to <b>digest</b>, or NULL if no such authority
* is known.
*/
-dir_server_t *
-trusteddirserver_get_by_v3_auth_digest(const char *digest)
+MOCK_IMPL(dir_server_t *,
+trusteddirserver_get_by_v3_auth_digest, (const char *digest))
{
if (!trusted_dir_servers)
return NULL;
diff --git a/src/or/routerlist.h b/src/or/routerlist.h
index be242d6..17adade 100644
--- a/src/or/routerlist.h
+++ b/src/or/routerlist.h
@@ -52,7 +52,8 @@ dir_server_t *router_get_trusteddirserver_by_digest(const char *d);
dir_server_t *router_get_fallback_dirserver_by_digest(
const char *digest);
int router_digest_is_fallback_dir(const char *digest);
-dir_server_t *trusteddirserver_get_by_v3_auth_digest(const char *d);
+MOCK_DECL(dir_server_t *, trusteddirserver_get_by_v3_auth_digest,
+ (const char *d));
const routerstatus_t *router_pick_trusteddirserver(dirinfo_type_t type,
int flags);
const routerstatus_t *router_pick_fallback_dirserver(dirinfo_type_t type,
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 5a9c123..18d45b5 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -24,6 +24,18 @@ get_my_v3_authority_cert_m(void)
return mock_cert;
}
+static dir_server_t ds;
+
+static dir_server_t *
+trusteddirserver_get_by_v3_auth_digest_m(const char *digest)
+{
+ (void) digest;
+ /* The shared random code only need to know if a valid pointer to a dir
+ * server object has been found so this is safe because it won't use the
+ * pointer at all never. */
+ return &ds;
+}
+
/* Setup a minimal dirauth environment by initializing the SR state and
* making sure the options are set to be an authority directory. */
static void
@@ -265,6 +277,9 @@ test_sr_commit(void *arg)
(void) arg;
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
{ /* Setup a minimal dirauth environment for this test */
or_options_t *options = get_options_mutable();
@@ -329,6 +344,7 @@ test_sr_commit(void *arg)
* takes from a vote line and see if we can parse it correctly. */
{
sr_commit_t *parsed_commit;
+ smartlist_add(args, tor_strdup("1"));
smartlist_add(args,
tor_strdup(crypto_digest_algorithm_get_name(our_commit->alg)));
smartlist_add(args, tor_strdup(sr_commit_get_rsa_fpr(our_commit)));
@@ -336,10 +352,13 @@ test_sr_commit(void *arg)
smartlist_add(args, our_commit->encoded_reveal);
parsed_commit = sr_parse_commit(args);
tt_assert(parsed_commit);
- /* That parsed commit should be _EXACTLY_ like our original commit. */
+ /* That parsed commit should be _EXACTLY_ like our original commit (we
+ * have to explicitly set the valid flag though). */
+ parsed_commit->valid = 1;
tt_mem_op(parsed_commit, OP_EQ, our_commit, sizeof(*parsed_commit));
/* Cleanup */
tor_free(smartlist_get(args, 0)); /* strdup here. */
+ tor_free(smartlist_get(args, 1)); /* strdup here. */
smartlist_clear(args);
sr_commit_free(parsed_commit);
}
@@ -347,6 +366,7 @@ test_sr_commit(void *arg)
done:
smartlist_free(args);
sr_commit_free(our_commit);
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
}
/* Test the encoding and decoding function for commit and reveal values. */
@@ -464,6 +484,9 @@ test_vote(void *arg)
(void) arg;
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
{ /* Setup a minimal dirauth environment for this test */
init_authority_state();
/* Set ourself in reveal phase so we can parse the reveal value in the
@@ -499,21 +522,23 @@ test_vote(void *arg)
tt_str_op(smartlist_get(chunks, 0), OP_EQ, "shared-rand-participate");
/* Get our commitment line and will validate it agains our commit. The
* format is as follow:
- * "shared-rand-commitment" SP identity SP algname SP COMMIT [SP REVEAL] NL
+ * "shared-rand-commitment" SP version SP algname SP identity
+ * SP COMMIT [SP REVEAL] NL
*/
char *commit_line = smartlist_get(chunks, 1);
tt_assert(commit_line);
ret = smartlist_split_string(tokens, commit_line, " ", 0, 0);
- tt_int_op(ret, ==, 5);
+ tt_int_op(ret, ==, 6);
tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-commit");
- tt_str_op(smartlist_get(tokens, 1), OP_EQ,
+ tt_str_op(smartlist_get(tokens, 1), OP_EQ, "1");
+ tt_str_op(smartlist_get(tokens, 2), OP_EQ,
crypto_digest_algorithm_get_name(DIGEST_SHA3_256));
char digest[DIGEST_LEN];
- base16_decode(digest, sizeof(digest), smartlist_get(tokens, 2),
+ base16_decode(digest, sizeof(digest), smartlist_get(tokens, 3),
HEX_DIGEST_LEN);
tt_mem_op(digest, ==, our_commit->rsa_identity, sizeof(digest));
- tt_str_op(smartlist_get(tokens, 3), OP_EQ, our_commit->encoded_commit);
- tt_str_op(smartlist_get(tokens, 4), OP_EQ, our_commit->encoded_reveal);
+ tt_str_op(smartlist_get(tokens, 4), OP_EQ, our_commit->encoded_commit);
+ tt_str_op(smartlist_get(tokens, 5), OP_EQ, our_commit->encoded_reveal);
/* Finally, does this vote line creates a valid commit object? */
smartlist_t *args = smartlist_new();
@@ -521,8 +546,12 @@ test_vote(void *arg)
smartlist_add(args, smartlist_get(tokens, 2));
smartlist_add(args, smartlist_get(tokens, 3));
smartlist_add(args, smartlist_get(tokens, 4));
+ smartlist_add(args, smartlist_get(tokens, 5));
sr_commit_t *parsed_commit = sr_parse_commit(args);
tt_assert(parsed_commit);
+ /* Set valid flag explicitly here to compare since it's not set by
+ * simply parsing the commit. */
+ parsed_commit->valid = 1;
tt_mem_op(parsed_commit, ==, our_commit, sizeof(*our_commit));
/* minor cleanup */
@@ -565,20 +594,22 @@ test_vote(void *arg)
done:
sr_commit_free(our_commit);
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
}
const char *sr_state_str = "Version 1\n"
- "ValidUntil 2666-04-20 07:16:00\n"
- "ValidAfter 2666-04-19 07:16:00\n"
- "Commit sha3-256 FA3CEC2C99DC68D3166B9B6E4FA21A4026C2AB1C "
+ "TorVersion 0.2.9.0-alpha-dev\n"
+ "ValidAfter 2037-04-19 07:16:00\n"
+ "ValidUntil 2037-04-20 07:16:00\n"
+ "Commit 1 sha3-256 FA3CEC2C99DC68D3166B9B6E4FA21A4026C2AB1C "
"7M8GdubCAAdh7WUG0DiwRyxTYRKji7HATa7LLJEZ/UAAAAAAVmfUSg== "
"AAAAAFZn1EojfIheIw42bjK3VqkpYyjsQFSbv/dxNna3Q8hUEPKpOw==\n"
- "Commit sha3-256 41E89EDFBFBA44983E21F18F2230A4ECB5BFB543 "
+ "Commit 1 sha3-256 41E89EDFBFBA44983E21F18F2230A4ECB5BFB543 "
"17aUsYuMeRjd2N1r8yNyg7aHqRa6gf4z7QPoxxAZbp0AAAAAVmfUSg==\n"
- "Commit sha3-256 36637026573A04110CF3E6B1D201FB9A98B88734 "
+ "Commit 1 sha3-256 36637026573A04110CF3E6B1D201FB9A98B88734 "
"DDDYtripvdOU+XPEUm5xpU64d9IURSds1xSwQsgeB8oAAAAAVmfUSg==\n"
- "SharedRandCurrentValue 3 8dWeW12KEzTGEiLGgO1UVJ7Z91CekoRcxt6Q9KhnOFI=\n"
- "SharedRandPreviousValue 4 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=\n";
+ "SharedRandPreviousValue 4 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=\n"
+ "SharedRandCurrentValue 3 8dWeW12KEzTGEiLGgO1UVJ7Z91CekoRcxt6Q9KhnOFI=\n";
/** Create an SR disk state, parse it and validate that the parsing went
* well. Yes! */
@@ -592,6 +623,9 @@ test_state_load_from_disk(void *arg)
(void) arg;
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
/* First try with a nonexistent path. */
ret = disk_state_load_from_disk_impl("NONEXISTENTNONEXISTENT");
tt_assert(ret == -ENOENT);
@@ -608,7 +642,7 @@ test_state_load_from_disk(void *arg)
/* Try to load the directory itself. Should fail. */
ret = disk_state_load_from_disk_impl(dir);
- tt_assert(ret == -EINVAL);
+ tt_assert(ret == -EISDIR);
/* State should be non-existent at this point. */
the_sr_state = get_sr_state();
@@ -634,6 +668,7 @@ test_state_load_from_disk(void *arg)
done:
tor_free(dir);
tor_free(sr_state_path);
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
}
/** Generate three specially crafted commits (based on the test
@@ -757,6 +792,9 @@ test_sr_compute_srv(void *arg)
MOCK(trusteddirserver_get_by_v3_auth_digest,
trusteddirserver_get_by_v3_auth_digest_m);
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
init_authority_state();
/* Setup the commits for this unittest */
@@ -778,7 +816,7 @@ test_sr_compute_srv(void *arg)
SRV_TEST_VECTOR);
done:
- ;
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
}
/** Return a minimal vote document with a current SRV value set to
1
0
commit b12d4852efaf6a6a5184abddacb777d30bf9850f
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 3 11:47:13 2016 -0400
prop250: Add changes file
Fixes #16943
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
Signed-off-by: George Kadianakis <desnacked(a)riseup.net>
---
changes/bug16943 | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/changes/bug16943 b/changes/bug16943
new file mode 100644
index 0000000..7b96d1a
--- /dev/null
+++ b/changes/bug16943
@@ -0,0 +1,8 @@
+ o Major features (dirauths, security, hidden services):
+ - Directory authorities can now perform the shared randomness protocol
+ specified by proposal 250. Using this protocol, directory authorities can
+ generate a global fresh random number every day. In the future, this
+ global randomness will be used by hidden services to select their
+ responsible HSDirs. This release only implements the directory authority
+ feature; the hidden service side will be implemented in the future as
+ part of proposal 224 . Resolves ticket #16943 and proposal 250.
1
0

01 Jul '16
commit 5fe9a50c310b8af4c9447475ec9e3748b24984f0
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 17 16:10:20 2016 -0400
prop250: Pass the dst length to sr_srv_encode()
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 12 +++++++-----
src/or/shared_random.h | 2 +-
src/or/shared_random_state.c | 2 +-
3 files changed, 9 insertions(+), 7 deletions(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 23c2b14..8427b68 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -437,7 +437,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, srv);
+ sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
log_debug(LD_DIR, "SR: Generated SRV: %s", srv_hash_encoded);
}
return srv;
@@ -504,7 +504,7 @@ srv_to_ns_string(const sr_srv_t *srv, const char *key)
tor_assert(srv);
tor_assert(key);
- sr_srv_encode(srv_hash_encoded, srv);
+ sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
tor_asprintf(&srv_str, "%s %d %s\n", key,
srv->num_reveals, srv_hash_encoded);
log_debug(LD_DIR, "SR: Consensus SRV line: %s", srv_str);
@@ -839,7 +839,7 @@ get_majority_srv_from_votes(const smartlist_t *votes, int current)
{
/* Debugging */
char encoded[SR_SRV_VALUE_BASE64_LEN + 1];
- sr_srv_encode(encoded, the_srv);
+ sr_srv_encode(encoded, sizeof(encoded), the_srv);
log_debug(LD_DIR, "SR: Chosen SRV by majority: %s (%d votes)", encoded,
count);
}
@@ -853,7 +853,7 @@ get_majority_srv_from_votes(const smartlist_t *votes, int current)
/* 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)
+sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
{
int ret;
/* Extra byte for the NULL terminated char. */
@@ -861,12 +861,14 @@ sr_srv_encode(char *dst, const sr_srv_t *srv)
tor_assert(dst);
tor_assert(srv);
+ tor_assert(dst_len >= sizeof(buf));
ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
sizeof(srv->value), 0);
/* Always expect the full length without the NULL byte. */
tor_assert(ret == (sizeof(buf) - 1));
- strlcpy(dst, buf, sizeof(buf));
+ tor_assert(ret <= (int) dst_len);
+ strlcpy(dst, buf, dst_len);
}
/* Free a commit object. */
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index f89f47a..4b16d26 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -114,7 +114,7 @@ sr_srv_t *sr_parse_srv(const smartlist_t *args);
char *sr_get_string_for_vote(void);
char *sr_get_string_for_consensus(const smartlist_t *votes);
void sr_commit_free(sr_commit_t *commit);
-void sr_srv_encode(char *dst, const sr_srv_t *srv);
+void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv);
/* Private methods (only used by shared_random_state.c): */
static inline
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index cce0c99..705c586 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -589,7 +589,7 @@ disk_state_put_srv_line(const sr_srv_t *srv, config_line_t *line)
if (srv == NULL) {
return;
}
- sr_srv_encode(encoded, srv);
+ sr_srv_encode(encoded, sizeof(encoded), srv);
tor_asprintf(&line->value, "%d %s", srv->num_reveals, encoded);
}
1
0
commit 0c26a6db7e683c23505c1c9187fd538c7b80ce31
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 3 11:36:09 2016 -0400
prop250: Parse votes and consensus
One of the last piece that parses the votes and consensus in order to update
our state and make decision for the SR values.
We need to inform the SR subsystem when we set the current consensus because
this can be called when loaded from file or downloaded from other authorities
or computed.
The voting schedule is used for the SR timings since we are bound to the
voting system.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
Signed-off-by: George Kadianakis <desnacked(a)riseup.net>
---
src/or/directory.c | 5 +
src/or/dirvote.c | 7 +
src/or/routerparse.c | 158 +++++++++++++++++
src/or/shared_random.c | 395 ++++++++++++++++++++++++++++++++++++++++++-
src/or/shared_random.h | 16 ++
src/or/shared_random_state.c | 29 ++++
src/or/shared_random_state.h | 1 +
7 files changed, 609 insertions(+), 2 deletions(-)
diff --git a/src/or/directory.c b/src/or/directory.c
index 876eaa4..b32090a 100644
--- a/src/or/directory.c
+++ b/src/or/directory.c
@@ -30,6 +30,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
+#include "shared_random.h"
#if defined(EXPORTMALLINFO) && defined(HAVE_MALLOC_H) && defined(HAVE_MALLINFO)
#ifndef OPENBSD
@@ -2026,6 +2027,10 @@ connection_dir_client_reached_eof(dir_connection_t *conn)
update_microdescs_from_networkstatus(now);
update_microdesc_downloads(now);
directory_info_has_arrived(now, 0, 0);
+ if (authdir_mode_v3(get_options())) {
+ sr_act_post_consensus(
+ networkstatus_get_latest_consensus_by_flavor(FLAV_NS));
+ }
log_info(LD_DIR, "Successfully loaded consensus.");
}
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index af093de..4eccdd0 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -2668,6 +2668,9 @@ dirvote_act(const or_options_t *options, time_t now)
dirvote_publish_consensus();
dirvote_clear_votes(0);
voting_schedule.have_published_consensus = 1;
+ /* Update our shared random state with the consensus just published. */
+ sr_act_post_consensus(
+ networkstatus_get_latest_consensus_by_flavor(FLAV_NS));
/* XXXX We will want to try again later if we haven't got enough
* signatures yet. Implement this if it turns out to ever happen. */
dirvote_recalculate_timing(options, now);
@@ -3007,6 +3010,10 @@ dirvote_add_vote(const char *vote_body, const char **msg_out, int *status_out)
}
} SMARTLIST_FOREACH_END(v);
+ /* This a valid vote, update our shared random state. */
+ sr_handle_received_commits(vote->sr_info.commits,
+ vote->cert->identity_key);
+
pending_vote = tor_malloc_zero(sizeof(pending_vote_t));
pending_vote->vote_body = new_cached_dir(tor_strndup(vote_body,
end_of_vote-vote_body),
diff --git a/src/or/routerparse.c b/src/or/routerparse.c
index 130f9f4..88cfbdc 100644
--- a/src/or/routerparse.c
+++ b/src/or/routerparse.c
@@ -28,6 +28,7 @@
#include "routerparse.h"
#include "entrynodes.h"
#include "torcert.h"
+#include "shared_random.h"
#undef log
#include <math.h>
@@ -145,6 +146,11 @@ typedef enum {
K_CONSENSUS_METHOD,
K_LEGACY_DIR_KEY,
K_DIRECTORY_FOOTER,
+ K_SIGNING_CERT_ED,
+ K_SR_FLAG,
+ K_COMMIT,
+ K_PREVIOUS_SRV,
+ K_CURRENT_SRV,
K_PACKAGE,
A_PURPOSE,
@@ -446,6 +452,11 @@ static token_rule_t networkstatus_token_table[] = {
T1("known-flags", K_KNOWN_FLAGS, ARGS, NO_OBJ ),
T01("params", K_PARAMS, ARGS, NO_OBJ ),
T( "fingerprint", K_FINGERPRINT, CONCAT_ARGS, NO_OBJ ),
+ T01("signing-ed25519", K_SIGNING_CERT_ED, NO_ARGS , NEED_OBJ ),
+ T01("shared-rand-participate",K_SR_FLAG, NO_ARGS, NO_OBJ ),
+ T0N("shared-rand-commit", K_COMMIT, GE(3), NO_OBJ ),
+ T01("shared-rand-previous-value", K_PREVIOUS_SRV,EQ(2), NO_OBJ ),
+ T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
T0N("package", K_PACKAGE, CONCAT_ARGS, NO_OBJ ),
CERTIFICATE_MEMBERS
@@ -485,6 +496,9 @@ static token_rule_t networkstatus_consensus_token_table[] = {
T01("consensus-method", K_CONSENSUS_METHOD, EQ(1), NO_OBJ),
T01("params", K_PARAMS, ARGS, NO_OBJ ),
+ T01("shared-rand-previous-value", K_PREVIOUS_SRV, EQ(2), NO_OBJ ),
+ T01("shared-rand-current-value", K_CURRENT_SRV, EQ(2), NO_OBJ ),
+
END_OF_TABLE
};
@@ -2843,6 +2857,134 @@ networkstatus_verify_bw_weights(networkstatus_t *ns, int consensus_method)
return valid;
}
+/** Parse and extract all SR commits from <b>tokens</b> and place them in
+ * <b>ns</b>. */
+static void
+extract_shared_random_commits(networkstatus_t *ns, smartlist_t *tokens)
+{
+ smartlist_t *chunks = NULL;
+
+ tor_assert(ns);
+ tor_assert(tokens);
+ /* Commits are only present in a vote. */
+ tor_assert(ns->type == NS_TYPE_VOTE);
+
+ ns->sr_info.commits = smartlist_new();
+
+ smartlist_t *commits = find_all_by_keyword(tokens, K_COMMIT);
+ /* It's normal that a vote might contain no commits even if it participates
+ * in the SR protocol. Don't treat it as an error. */
+ if (commits == NULL) {
+ goto end;
+ }
+
+ /* Parse the commit. We do NO validation of number of arguments or ordering
+ * for forward compatibility, it's the parse commit job to inform us if it's
+ * supported or not. */
+ chunks = smartlist_new();
+ SMARTLIST_FOREACH_BEGIN(commits, directory_token_t *, tok) {
+ /* Extract all arguments and put them in the chunks list. */
+ for (int i = 0; i < tok->n_args; i++) {
+ smartlist_add(chunks, tok->args[i]);
+ }
+ sr_commit_t *commit = sr_parse_commit(chunks);
+ smartlist_clear(chunks);
+ if (commit == NULL) {
+ /* Get voter identity so we can warn that this dirauth vote contains
+ * commit we can't parse. */
+ networkstatus_voter_info_t *voter = smartlist_get(ns->voters, 0);
+ tor_assert(voter);
+ log_warn(LD_DIR, "SR: Unable to parse commit %s from vote of voter %s.",
+ escaped(tok->object_body),
+ hex_str(voter->identity_digest,
+ sizeof(voter->identity_digest)));
+ /* Commitment couldn't be parsed. Continue onto the next commit because
+ * this one could be unsupported for instance. */
+ continue;
+ }
+ /* Add newly created commit object to the vote. */
+ smartlist_add(ns->sr_info.commits, commit);
+ } SMARTLIST_FOREACH_END(tok);
+
+ end:
+ smartlist_free(chunks);
+ smartlist_free(commits);
+}
+
+/** Check if a shared random value of type <b>srv_type</b> is in
+ * <b>tokens</b>. If there is, parse it and set it to <b>srv_out</b>. Return
+ * -1 on failure, 0 on success. The resulting srv is allocated on the heap and
+ * it's the responsibility of the caller to free it. */
+static int
+extract_one_srv(smartlist_t *tokens, directory_keyword srv_type,
+ sr_srv_t **srv_out)
+{
+ int ret = -1;
+ directory_token_t *tok;
+ sr_srv_t *srv = NULL;
+ smartlist_t *chunks;
+
+ tor_assert(tokens);
+
+ chunks = smartlist_new();
+ tok = find_opt_by_keyword(tokens, srv_type);
+ if (!tok) {
+ /* That's fine, no SRV is allowed. */
+ ret = 0;
+ goto end;
+ }
+ for (int i = 0; i < tok->n_args; i++) {
+ smartlist_add(chunks, tok->args[i]);
+ }
+ srv = sr_parse_srv(chunks);
+ if (srv == NULL) {
+ log_warn(LD_DIR, "SR: Unparseable SRV %s", escaped(tok->object_body));
+ goto end;
+ }
+ /* All is good. */
+ *srv_out = srv;
+ ret = 0;
+ end:
+ smartlist_free(chunks);
+ return ret;
+}
+
+/** Extract any shared random values found in <b>tokens</b> and place them in
+ * the networkstatus <b>ns</b>. */
+static void
+extract_shared_random_srvs(networkstatus_t *ns, smartlist_t *tokens)
+{
+ const char *voter_identity;
+ networkstatus_voter_info_t *voter;
+
+ tor_assert(ns);
+ tor_assert(tokens);
+ /* Can be only one of them else code flow. */
+ tor_assert(ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS);
+
+ if (ns->type == NS_TYPE_VOTE) {
+ voter = smartlist_get(ns->voters, 0);
+ tor_assert(voter);
+ voter_identity = hex_str(voter->identity_digest,
+ sizeof(voter->identity_digest));
+ } else {
+ /* Consensus has multiple voters so no specific voter. */
+ voter_identity = "consensus";
+ }
+
+ /* We extract both and on error, everything is stopped because it means
+ * the votes is malformed for the shared random value(s). */
+ if (extract_one_srv(tokens, K_PREVIOUS_SRV, &ns->sr_info.previous_srv) < 0) {
+ log_warn(LD_DIR, "SR: Unable to parse previous SRV from %s",
+ voter_identity);
+ /* Maybe we have a chance with the current SRV so let's try it anyway. */
+ }
+ if (extract_one_srv(tokens, K_CURRENT_SRV, &ns->sr_info.current_srv) < 0) {
+ log_warn(LD_DIR, "SR: Unable to parse current SRV from %s",
+ voter_identity);
+ }
+}
+
/** Parse a v3 networkstatus vote, opinion, or consensus (depending on
* ns_type), from <b>s</b>, and return the result. Return NULL on failure. */
networkstatus_t *
@@ -3187,6 +3329,22 @@ networkstatus_parse_vote_from_string(const char *s, const char **eos_out,
}
}
+ /* If this is a vote document, check if information about the shared
+ randomness protocol is included, and extract it. */
+ if (ns->type == NS_TYPE_VOTE) {
+ /* Does this authority participates in the SR protocol? */
+ tok = find_opt_by_keyword(tokens, K_SR_FLAG);
+ if (tok) {
+ ns->sr_info.participate = 1;
+ /* Get the SR commitments and reveals from the vote. */
+ extract_shared_random_commits(ns, tokens);
+ }
+ }
+ /* For both a vote and consensus, extract the shared random values. */
+ if (ns->type == NS_TYPE_VOTE || ns->type == NS_TYPE_CONSENSUS) {
+ extract_shared_random_srvs(ns, tokens);
+ }
+
/* Parse routerstatus lines. */
rs_tokens = smartlist_new();
rs_area = memarea_new();
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 0f988af..e3fe1a9 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -6,6 +6,83 @@
*
* \brief Functions and data structure needed to accomplish the shared
* random protocol as defined in proposal #250.
+ *
+ * \details
+ *
+ * This file implements the dirauth-only commit-and-reveal protocol specified
+ * by proposal #250. The protocol has two phases (sr_phase_t): the commitment
+ * phase and the reveal phase (see get_sr_protocol_phase()).
+ *
+ * During the protocol, directory authorities keep state in memory (using
+ * sr_state_t) and in disk (using sr_disk_state_t). The synchronization between
+ * these two data structures happens in disk_state_update() and
+ * disk_state_parse().
+ *
+ * Here is a rough protocol outline:
+ *
+ * 1) In the beginning of the commitment phase, dirauths generate a
+ * commitment/reveal value for the current protocol run (see
+ * new_protocol_run() and sr_generate_our_commit()).
+ *
+ * 2) During voting, dirauths publish their commits in their votes
+ * depending on the current phase. Dirauths also include the two
+ * latest shared random values (SRV) in their votes.
+ * (see sr_get_string_for_vote())
+ *
+ * 3) Upon receiving a commit from a vote, authorities parse it, verify
+ * it, and attempt to save any new commitment or reveal information in
+ * their state file (see extract_shared_random_commits() and
+ * sr_handle_received_commits()). They also parse SRVs from votes to
+ * decide which SRV should be included in the final consensus (see
+ * extract_shared_random_srvs()).
+ *
+ * 3) After voting is done, we count the SRVs we extracted from the votes,
+ * to find the one voted by the majority of dirauths which should be
+ * included in the final consensus (see get_majority_srv_from_votes()).
+ * If an appropriate SRV is found, it is embedded in the consensus (see
+ * sr_get_string_for_consensus()).
+ *
+ * 4) At the end of the reveal phase, dirauths compute a fresh SRV for the
+ * day using the active commits (see sr_compute_srv()). This new SRV
+ * is embedded in the votes as described above.
+ *
+ * Some more notes:
+ *
+ * - To support rebooting authorities and to avoid double voting, each dirauth
+ * saves the current state of the protocol on disk so that it can resume
+ * normally in case of reboot. The disk state (sr_disk_state_t) is managed by
+ * shared_random_state.c:state_query() and we go to extra lengths to ensure
+ * that the state is flushed on disk everytime we receive any useful
+ * information like commits or SRVs.
+ *
+ * - When we receive a commit from a vote, we examine it to see if it's useful
+ * to us and whether it's appropriate to receive it according to the current
+ * phase of the protocol (see should_keep_commit()). If the commit is useful
+ * to us, we save it in our disk state using save_commit_to_state(). When we
+ * receive the reveal information corresponding to a commitment, we verify
+ * that they indeed match using verify_commit_and_reveal().
+ *
+ * - We treat consensuses as the ground truth, so everytime we generate a new
+ * consensus we update our SR state accordingly even if our local view was
+ * different (see sr_act_post_consensus()).
+ *
+ * - After a consensus has been composed, the SR protocol state gets prepared
+ * for the next voting session using sr_state_update(). That function takes
+ * care of housekeeping and also rotates the SRVs and commits in case a new
+ * protocol run is coming up. We also call sr_state_update() on bootup (in
+ * sr_state_init()), to prepare the state for the very first voting session.
+ *
+ * Terminology:
+ *
+ * - "Commitment" is the commitment value of the commit-and-reveal protocol.
+ *
+ * - "Reveal" is the reveal value of the commit-and-reveal protocol.
+ *
+ * - "Commit" is a struct (sr_commit_t) that contains a commitment value and
+ * optionally also a corresponding reveal value.
+ *
+ * - "SRV" is the Shared Random Value that gets generated as the result of the
+ * commit-and-reveal protocol.
**/
#define SHARED_RANDOM_PRIVATE
@@ -27,6 +104,22 @@ static const char current_srv_str[] = "shared-rand-current-value";
static const char commit_ns_str[] = "shared-rand-commit";
static const char sr_flag_ns_str[] = "shared-rand-participate";
+/* Return a heap allocated copy of the SRV <b>orig</b>. */
+STATIC sr_srv_t *
+srv_dup(const sr_srv_t *orig)
+{
+ sr_srv_t *dup = NULL;
+
+ if (!orig) {
+ return NULL;
+ }
+
+ dup = tor_malloc_zero(sizeof(sr_srv_t));
+ dup->num_reveals = orig->num_reveals;
+ memcpy(dup->value, orig->value, sizeof(dup->value));
+ return dup;
+}
+
/* Allocate a new commit object and initializing it with <b>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. */
@@ -65,6 +158,59 @@ commit_log(const sr_commit_t *commit)
}
}
+/* Make sure that the commitment and reveal information in <b>commit</b>
+ * match. If they match return 0, return -1 otherwise. This function MUST be
+ * used everytime we receive a new reveal value. Furthermore, the commit
+ * object MUST have a reveal value and the hash of the reveal value. */
+STATIC int
+verify_commit_and_reveal(const sr_commit_t *commit)
+{
+ tor_assert(commit);
+
+ log_debug(LD_DIR, "SR: Validating commit from authority %s",
+ commit->rsa_identity_fpr);
+
+ /* 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);
+ goto invalid;
+ }
+
+ /* Verify that the hashed_reveal received in the COMMIT message, matches
+ * the reveal we just received. */
+ {
+ /* We first hash the reveal we just received. */
+ char received_hashed_reveal[sizeof(commit->hashed_reveal)];
+
+ /* Only sha3-256 is supported. */
+ if (commit->alg != SR_DIGEST_ALG) {
+ goto invalid;
+ }
+
+ /* Use the invariant length since the encoded reveal variable has an
+ * extra byte for the NUL terminated byte. */
+ if (crypto_digest256(received_hashed_reveal, commit->encoded_reveal,
+ SR_REVEAL_BASE64_LEN, commit->alg)) {
+ /* Unable to digest the reveal blob, this is unlikely. */
+ goto invalid;
+ }
+
+ /* Now compare that with the hashed_reveal we received in COMMIT. */
+ if (fast_memneq(received_hashed_reveal, commit->hashed_reveal,
+ sizeof(received_hashed_reveal))) {
+ log_warn(LD_BUG, "SR: Received reveal value from authority %s "
+ "does't match the commit value.",
+ commit->rsa_identity_fpr);
+ goto invalid;
+ }
+ }
+
+ return 0;
+ invalid:
+ return -1;
+}
+
/* Return true iff the commit contains an encoded reveal value. */
STATIC int
commit_has_reveal_value(const sr_commit_t *commit)
@@ -403,8 +549,176 @@ get_ns_str_from_sr_values(const sr_srv_t *prev_srv, const sr_srv_t *cur_srv)
return srv_str;
}
-/* Return the number of required participants of the SR protocol. This is
- * based on a consensus params. */
+/* Return 1 iff the two commits have the same commitment values. This
+ * function does not care about reveal values. */
+STATIC int
+commitments_are_the_same(const sr_commit_t *commit_one,
+ const sr_commit_t *commit_two)
+{
+ tor_assert(commit_one);
+ tor_assert(commit_two);
+
+ if (strcmp(commit_one->encoded_commit, commit_two->encoded_commit)) {
+ return 0;
+ }
+ return 1;
+}
+
+/* We just received a commit from the vote of authority with
+ * <b>identity_digest</b>. Return 1 if this commit is authorititative that
+ * is, it belongs to the authority that voted it. Else return 0 if not. */
+STATIC int
+commit_is_authoritative(const sr_commit_t *commit,
+ const char *voter_key)
+{
+ tor_assert(commit);
+ tor_assert(voter_key);
+
+ return !strcmp(commit->rsa_identity_fpr, voter_key);
+}
+
+/* Decide if the newly received <b>commit</b> should be kept depending on
+ * the current phase and state of the protocol. The <b>voter_key</b> is the
+ * RSA identity key fingerprint of the authority's vote from which the
+ * commit comes from. The <b>phase</b> is the phase we should be validating
+ * the commit for. Return 1 if the commit should be added to our state or 0
+ * if not. */
+STATIC int
+should_keep_commit(const sr_commit_t *commit, const char *voter_key,
+ sr_phase_t phase)
+{
+ sr_commit_t *saved_commit;
+
+ tor_assert(commit);
+ tor_assert(voter_key);
+
+ log_debug(LD_DIR, "SR: Inspecting commit from %s (voter: %s)?",
+ commit->rsa_identity_fpr, voter_key);
+
+ /* For a commit to be considered, it needs to be authoritative (it should
+ * be the voter's own commit). */
+ if (!commit_is_authoritative(commit, voter_key)) {
+ log_debug(LD_DIR, "SR: Ignoring non-authoritative commit.");
+ goto ignore;
+ }
+
+ /* Check if the authority that voted for <b>commit</b> has already posted
+ * a commit before. */
+ saved_commit = sr_state_get_commit(commit->rsa_identity_fpr);
+
+ switch (phase) {
+ 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.");
+ goto ignore;
+ }
+
+ /* A commit with a reveal value during commitment phase is very wrong. */
+ if (commit_has_reveal_value(commit)) {
+ log_warn(LD_DIR, "SR: Commit from authority %s has a reveal value "
+ "during COMMIT phase. (voter: %s)",
+ commit->rsa_identity_fpr, voter_key);
+ goto ignore;
+ }
+ break;
+ case SR_PHASE_REVEAL:
+ /* We are now in reveal phase. We keep a commit if and only if:
+ *
+ * - We have already seen a commit by this auth, AND
+ * - the saved commit has the same commitment value as this one, AND
+ * - the saved commit has no reveal information, AND
+ * - this commit does have reveal information, AND
+ * - the reveal & commit information are matching.
+ *
+ * If all the above are true, then we are interested in this new commit
+ * for its reveal information. */
+
+ if (!saved_commit) {
+ log_debug(LD_DIR, "SR: Ignoring commit first seen in reveal phase.");
+ goto ignore;
+ }
+
+ if (!commitments_are_the_same(commit, saved_commit)) {
+ log_warn(LD_DIR, "SR: Commit from authority %s is different from "
+ "previous commit in our state (voter: %s)",
+ commit->rsa_identity_fpr, voter_key);
+ goto ignore;
+ }
+
+ if (commit_has_reveal_value(saved_commit)) {
+ log_debug(LD_DIR, "SR: Ignoring commit with known reveal info.");
+ goto ignore;
+ }
+
+ if (!commit_has_reveal_value(commit)) {
+ log_debug(LD_DIR, "SR: Ignoring commit without reveal value.");
+ goto ignore;
+ }
+
+ if (verify_commit_and_reveal(commit) < 0) {
+ log_warn(LD_BUG, "SR: Commit from authority %s has an invalid "
+ "reveal value. (voter: %s)",
+ commit->rsa_identity_fpr, voter_key);
+ goto ignore;
+ }
+ break;
+ default:
+ tor_assert(0);
+ }
+
+ return 1;
+
+ ignore:
+ return 0;
+}
+
+/* We are in reveal phase and we found a valid and verified <b>commit</b> in
+ * a vote that contains reveal values that we could use. Update the commit
+ * we have in our state. Never call this with an unverified commit. */
+STATIC void
+save_commit_during_reveal_phase(const sr_commit_t *commit)
+{
+ sr_commit_t *saved_commit;
+
+ tor_assert(commit);
+
+ /* Get the commit from our state. */
+ saved_commit = sr_state_get_commit(commit->rsa_identity_fpr);
+ tor_assert(saved_commit);
+ /* Safety net. They can not be different commitments at this point. */
+ int same_commits = commitments_are_the_same(commit, saved_commit);
+ tor_assert(same_commits);
+
+ /* Copy reveal information to our saved commit. */
+ sr_state_copy_reveal_info(saved_commit, commit);
+}
+
+/* Save <b>commit</b> to our persistent state. Depending on the current
+ * phase, different actions are taken. Steals reference of <b>commit</b>.
+ * The commit object MUST be valid and verified before adding it to the
+ * state. */
+STATIC void
+save_commit_to_state(sr_commit_t *commit)
+{
+ sr_phase_t phase = sr_state_get_phase();
+
+ switch (phase) {
+ case SR_PHASE_COMMIT:
+ /* During commit phase, just save any new authoritative commit */
+ sr_state_add_commit(commit);
+ break;
+ case SR_PHASE_REVEAL:
+ save_commit_during_reveal_phase(commit);
+ sr_commit_free(commit);
+ break;
+ default:
+ tor_assert(0);
+ }
+}
+
+/* Return the number of required participants of the SR protocol. This is based
+ * on a consensus params. */
static int
get_n_voters_for_srv_agreement(void)
{
@@ -789,6 +1103,41 @@ sr_parse_commit(const smartlist_t *args)
return NULL;
}
+/* Called when we are done parsing a vote by <b>voter_key</b> that might
+ * contain some useful <b>commits</b>. Find if any of them should be kept
+ * and update our state accordingly. Once done, the list of commitments will
+ * be empty. */
+void
+sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key)
+{
+ char rsa_identity_fpr[FINGERPRINT_LEN + 1];
+
+ tor_assert(voter_key);
+
+ /* It's possible that the vote has _NO_ commits. */
+ if (commits == NULL) {
+ return;
+ }
+
+ /* Get the RSA identity fingerprint of this voter */
+ if (crypto_pk_get_fingerprint(voter_key, rsa_identity_fpr, 0) < 0) {
+ return;
+ }
+
+ SMARTLIST_FOREACH_BEGIN(commits, sr_commit_t *, commit) {
+ /* We won't need the commit in this list anymore, kept or not. */
+ SMARTLIST_DEL_CURRENT(commits, commit);
+ /* Check if this commit is valid and should be stored in our state. */
+ if (!should_keep_commit(commit, rsa_identity_fpr,
+ sr_state_get_phase())) {
+ sr_commit_free(commit);
+ continue;
+ }
+ /* Everything lines up: save this commit to state then! */
+ save_commit_to_state(commit);
+ } SMARTLIST_FOREACH_END(commit);
+}
+
/* Return a heap-allocated string containing commits that should be put in
* the votes. It's the responsibility of the caller to free the string.
* This always return a valid string, either empty or with line(s). */
@@ -873,6 +1222,48 @@ sr_get_string_for_consensus(const smartlist_t *votes)
return NULL;
}
+/* We just computed a new <b>consensus</b>. Update our state with the SRVs
+ * from the consensus (might be NULL as well). Register the SRVs in our SR
+ * state and prepare for the upcoming protocol round. */
+void
+sr_act_post_consensus(const networkstatus_t *consensus)
+{
+ time_t interval_starts;
+ const or_options_t *options = get_options();
+
+ /* Don't act if our state hasn't been initialized. We can be called during
+ * boot time when loading consensus from disk which is prior to the
+ * initialization of the SR subsystem. We also should not be doing
+ * anything if we are _not_ a directory authority and if we are a bridge
+ * authority. */
+ if (!sr_state_is_initialized() || !authdir_mode_v3(options) ||
+ authdir_mode_bridge(options)) {
+ return;
+ }
+
+ /* Set the majority voted SRVs in our state even if both are NULL. It
+ * doesn't matter this is what the majority has decided. Obviously, we can
+ * only do that if we have a consensus. */
+ if (consensus) {
+ /* Start by freeing the current SRVs since the SRVs we believed during
+ * voting do not really matter. Now that all the votes are in, we use the
+ * majority's opinion on which are the active SRVs. */
+ sr_state_clean_srvs();
+ /* Reset the fresh flag of the SRV so we know that from now on we don't
+ * have a new SRV to vote for. We just used the one from the consensus
+ * decided by the majority. */
+ sr_state_unset_fresh_srv();
+ /* Set the SR values from the given consensus. */
+ sr_state_set_previous_srv(srv_dup(consensus->sr_info.previous_srv));
+ sr_state_set_current_srv(srv_dup(consensus->sr_info.current_srv));
+ }
+
+ /* Update our internal state with the next voting interval starting time. */
+ interval_starts = get_voting_schedule(options, time(NULL),
+ LOG_NOTICE)->interval_starts;
+ sr_state_update(interval_starts);
+}
+
/* Initialize shared random subsystem. This MUST be called early in the boot
* process of tor. Return 0 on success else -1 on error. */
int
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index f1fbe27..d52258c 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -101,6 +101,9 @@ typedef struct sr_commit_t {
int sr_init(int save_to_disk);
void sr_save_and_cleanup(void);
+void sr_act_post_consensus(const networkstatus_t *consensus);
+void sr_handle_received_commits(smartlist_t *commits,
+ crypto_pk_t *voter_key);
sr_commit_t *sr_parse_commit(const smartlist_t *args);
sr_srv_t *sr_parse_srv(const smartlist_t *args);
char *sr_get_string_for_vote(void);
@@ -125,9 +128,22 @@ STATIC int reveal_decode(const char *encoded, sr_commit_t *commit);
STATIC int commit_has_reveal_value(const sr_commit_t *commit);
+STATIC int verify_commit_and_reveal(const sr_commit_t *commit);
+
STATIC sr_srv_t *get_majority_srv_from_votes(const smartlist_t *votes,
int current);
+STATIC void save_commit_to_state(sr_commit_t *commit);
+STATIC sr_srv_t *srv_dup(const sr_srv_t *orig);
+STATIC int commitments_are_the_same(const sr_commit_t *commit_one,
+ const sr_commit_t *commit_two);
+STATIC int commit_is_authoritative(const sr_commit_t *commit,
+ const char *voter_key);
+STATIC int should_keep_commit(const sr_commit_t *commit,
+ const char *voter_key,
+ sr_phase_t phase);
+STATIC void save_commit_during_reveal_phase(const sr_commit_t *commit);
+
#endif /* SHARED_RANDOM_PRIVATE */
#endif /* TOR_SHARED_RANDOM_H */
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 87bdfc0..6dd10d6 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -1160,6 +1160,27 @@ sr_state_delete_commits(void)
state_query(SR_STATE_ACTION_DEL_ALL, SR_STATE_OBJ_COMMIT, NULL, NULL);
}
+/* Copy the reveal information from <b>commit</b> into <b>saved_commit</b>.
+ * This <b>saved_commit</b> MUST come from our current SR state. Once modified,
+ * the disk state is updated. */
+void
+sr_state_copy_reveal_info(sr_commit_t *saved_commit, const sr_commit_t *commit)
+{
+ tor_assert(saved_commit);
+ tor_assert(commit);
+
+ saved_commit->reveal_ts = commit->reveal_ts;
+ memcpy(saved_commit->random_number, commit->random_number,
+ sizeof(saved_commit->random_number));
+
+ strlcpy(saved_commit->encoded_reveal, commit->encoded_reveal,
+ sizeof(saved_commit->encoded_reveal));
+ state_query(SR_STATE_ACTION_SAVE, 0, NULL, NULL);
+ log_debug(LD_DIR, "SR: Reveal value learned %s (for commit %s) from %s",
+ saved_commit->encoded_reveal, saved_commit->encoded_commit,
+ saved_commit->rsa_identity_fpr);
+}
+
/* 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
@@ -1202,6 +1223,14 @@ sr_state_save(void)
state_query(SR_STATE_ACTION_SAVE, 0, NULL, NULL);
}
+/* Return 1 iff the state has been initialized that is it exists in memory.
+ * Return 0 otherwise. */
+int
+sr_state_is_initialized(void)
+{
+ return sr_state == NULL ? 0 : 1;
+}
+
/* Initialize the disk and memory state.
*
* If save_to_disk is set to 1, the state is immediately saved to disk after
diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h
index d9d9751..499a375 100644
--- a/src/or/shared_random_state.h
+++ b/src/or/shared_random_state.h
@@ -116,6 +116,7 @@ unsigned int sr_state_srv_is_fresh(void);
void sr_state_set_fresh_srv(void);
void sr_state_unset_fresh_srv(void);
int sr_state_init(int save_to_disk, int read_from_disk);
+int sr_state_is_initialized(void);
void sr_state_save(void);
void sr_state_free(void);
1
0

01 Jul '16
commit ca6ceec112f05ce68097429089ee428010c8b8d0
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 3 11:21:17 2016 -0400
prop250: Put commits and SRVs in votes/consensus
This commit adds the commit(s) line in the vote as well as the SR values. It
also has the mechanism to add the majority SRVs in the consensus.
Signed-off-by: George Kadianakis <desnacked(a)riseup.net>
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
doc/tor.1.txt | 6 +
src/or/config.c | 1 +
src/or/dirvote.c | 22 +++-
src/or/dirvote.h | 7 +-
src/or/networkstatus.c | 10 ++
src/or/or.h | 21 ++++
src/or/shared_random.c | 301 +++++++++++++++++++++++++++++++++++++++++++++++++
src/or/shared_random.h | 9 +-
8 files changed, 372 insertions(+), 5 deletions(-)
diff --git a/doc/tor.1.txt b/doc/tor.1.txt
index 65d3c2d..8508aaa 100644
--- a/doc/tor.1.txt
+++ b/doc/tor.1.txt
@@ -2225,6 +2225,12 @@ on the public Tor network.
in a journal if it is new, or if it differs from the most recently
accepted pinning for one of the keys it contains. (Default: 0)
+[[AuthDirSharedRandomness]] **AuthDirSharedRandomness** **0**|**1**::
+ Authoritative directories only. Switch for the shared random protocol.
+ If zero, the authority won't participate in the protocol. If non-zero
+ (default), the flag "shared-rand-participate" is added to the authority
+ vote indicating participation in the protocol. (Default: 1)
+
[[BridgePassword]] **BridgePassword** __Password__::
If set, contains an HTTP authenticator that tells a bridge authority to
serve all requested bridge information. Used by the (only partially
diff --git a/src/or/config.c b/src/or/config.c
index cdd4f10..e1a4473 100644
--- a/src/or/config.c
+++ b/src/or/config.c
@@ -439,6 +439,7 @@ static config_var_t option_vars_[] = {
V(UseNTorHandshake, AUTOBOOL, "1"),
V(User, STRING, NULL),
V(UserspaceIOCPBuffers, BOOL, "0"),
+ V(AuthDirSharedRandomness, BOOL, "1"),
OBSOLETE("V1AuthoritativeDirectory"),
OBSOLETE("V2AuthoritativeDirectory"),
VAR("V3AuthoritativeDirectory",BOOL, V3AuthoritativeDir, "0"),
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index a08f6ec..af093de 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -15,10 +15,12 @@
#include "policies.h"
#include "rephist.h"
#include "router.h"
+#include "routerkeys.h"
#include "routerlist.h"
#include "routerparse.h"
#include "entrynodes.h" /* needed for guardfraction methods */
#include "torcert.h"
+#include "shared_random_state.h"
/**
* \file dirvote.c
@@ -73,6 +75,7 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
char digest[DIGEST_LEN];
uint32_t addr;
char *client_versions_line = NULL, *server_versions_line = NULL;
+ char *shared_random_vote_str = NULL;
networkstatus_voter_info_t *voter;
char *status = NULL;
@@ -114,6 +117,9 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
packages = tor_strdup("");
}
+ /* Get shared random commitments/reveals line(s). */
+ shared_random_vote_str = sr_get_string_for_vote();
+
{
char published[ISO_TIME_LEN+1];
char va[ISO_TIME_LEN+1];
@@ -153,7 +159,8 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
"flag-thresholds %s\n"
"params %s\n"
"dir-source %s %s %s %s %d %d\n"
- "contact %s\n",
+ "contact %s\n"
+ "%s", /* shared randomness information */
v3_ns->type == NS_TYPE_VOTE ? "vote" : "opinion",
methods,
published, va, fu, vu,
@@ -166,12 +173,15 @@ format_networkstatus_vote(crypto_pk_t *private_signing_key,
params,
voter->nickname, fingerprint, voter->address,
fmt_addr32(addr), voter->dir_port, voter->or_port,
- voter->contact);
+ voter->contact,
+ shared_random_vote_str ?
+ shared_random_vote_str : "");
tor_free(params);
tor_free(flags);
tor_free(flag_thresholds);
tor_free(methods);
+ tor_free(shared_random_vote_str);
if (!tor_digest_is_zero(voter->legacy_id_digest)) {
char fpbuf[HEX_DIGEST_LEN+1];
@@ -1300,6 +1310,14 @@ networkstatus_compute_consensus(smartlist_t *votes,
smartlist_add(chunks, tor_strdup("\n"));
}
+ if (consensus_method >= MIN_METHOD_FOR_SHARED_RANDOM) {
+ /* Add the shared random value. */
+ char *srv_lines = sr_get_string_for_consensus(votes);
+ if (srv_lines != NULL) {
+ smartlist_add(chunks, srv_lines);
+ }
+ }
+
/* Sort the votes. */
smartlist_sort(votes, compare_votes_by_authority_id_);
/* Add the authority sections. */
diff --git a/src/or/dirvote.h b/src/or/dirvote.h
index f2080a5..4c563aa 100644
--- a/src/or/dirvote.h
+++ b/src/or/dirvote.h
@@ -55,7 +55,7 @@
#define MIN_SUPPORTED_CONSENSUS_METHOD 13
/** The highest consensus method that we currently support. */
-#define MAX_SUPPORTED_CONSENSUS_METHOD 22
+#define MAX_SUPPORTED_CONSENSUS_METHOD 23
/** Lowest consensus method where microdesc consensuses omit any entry
* with no microdesc. */
@@ -90,10 +90,15 @@
* ed25519 identities in microdescriptors. (Broken; see
* consensus_method_is_supported() for more info.) */
#define MIN_METHOD_FOR_ED25519_ID_IN_MD 21
+
/** Lowest consensus method where authorities vote on ed25519 ids and ensure
* ed25519 id consistency. */
#define MIN_METHOD_FOR_ED25519_ID_VOTING 22
+/** Lowest consensus method where authorities may include a shared random
+ * value(s). */
+#define MIN_METHOD_FOR_SHARED_RANDOM 23
+
/** Default bandwidth to clip unmeasured bandwidths to using method >=
* MIN_METHOD_TO_CLIP_UNMEASURED_BW. (This is not a consensus method; do not
* get confused with the above macros.) */
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index da51698..aabbfff 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -32,7 +32,9 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "shared_random.h"
#include "transports.h"
+#include "torcert.h"
/** Map from lowercase nickname to identity digest of named server, if any. */
static strmap_t *named_server_map = NULL;
@@ -320,6 +322,14 @@ networkstatus_vote_free(networkstatus_t *ns)
digestmap_free(ns->desc_digest_map, NULL);
+ if (ns->sr_info.commits) {
+ SMARTLIST_FOREACH(ns->sr_info.commits, sr_commit_t *, c,
+ sr_commit_free(c));
+ smartlist_free(ns->sr_info.commits);
+ }
+ tor_free(ns->sr_info.previous_srv);
+ tor_free(ns->sr_info.current_srv);
+
memwipe(ns, 11, sizeof(*ns));
tor_free(ns);
}
diff --git a/src/or/or.h b/src/or/or.h
index 5a19f9a..1b7f1a8 100644
--- a/src/or/or.h
+++ b/src/or/or.h
@@ -2505,6 +2505,18 @@ typedef struct networkstatus_voter_info_t {
smartlist_t *sigs;
} networkstatus_voter_info_t;
+typedef struct networkstatus_sr_info_t {
+ /* Indicate if the dirauth partitipates in the SR protocol with its vote.
+ * This is tied to the SR flag in the vote. */
+ unsigned int participate:1;
+ /* Both vote and consensus: Current and previous SRV. If list is empty,
+ * this means none were found in either the consensus or vote. */
+ struct sr_srv_t *previous_srv;
+ struct sr_srv_t *current_srv;
+ /* Vote only: List of commitments. */
+ smartlist_t *commits;
+} networkstatus_sr_info_t;
+
/** Enumerates the possible seriousness values of a networkstatus document. */
typedef enum {
NS_TYPE_VOTE,
@@ -2587,6 +2599,9 @@ typedef struct networkstatus_t {
/** If present, a map from descriptor digest to elements of
* routerstatus_list. */
digestmap_t *desc_digest_map;
+
+ /** Contains the shared random protocol data from a vote or consensus. */
+ networkstatus_sr_info_t sr_info;
} networkstatus_t;
/** A set of signatures for a networkstatus consensus. Unless otherwise
@@ -4480,6 +4495,12 @@ typedef struct {
/** Autobool: Do we try to retain capabilities if we can? */
int KeepBindCapabilities;
+
+ /** Bool (default: 1): Switch for the shared random protocol. Only
+ * relevant to a directory authority. If off, the authority won't
+ * participate in the protocol. If on (default), a flag is added to the
+ * vote indicating participation. */
+ int AuthDirSharedRandomness;
} or_options_t;
/** Persistent state for an onion router, as saved to disk. */
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index dd567bc..0f988af 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -14,12 +14,19 @@
#include "shared_random.h"
#include "config.h"
#include "confparse.h"
+#include "dirvote.h"
#include "networkstatus.h"
#include "routerkeys.h"
#include "router.h"
#include "routerlist.h"
#include "shared_random_state.h"
+/* String prefix of shared random values in votes/consensuses. */
+static const char previous_srv_str[] = "shared-rand-previous-value";
+static const char current_srv_str[] = "shared-rand-current-value";
+static const char commit_ns_str[] = "shared-rand-commit";
+static const char sr_flag_ns_str[] = "shared-rand-participate";
+
/* 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. */
@@ -307,6 +314,216 @@ compare_reveal_(const void **_a, const void **_b)
sizeof(a->hashed_reveal));
}
+/* Given <b>commit</b> give the line that we should place in our votes.
+ * It's the responsibility of the caller to free the string. */
+static char *
+get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
+{
+ char *vote_line = NULL;
+
+ switch (phase) {
+ case SR_PHASE_COMMIT:
+ tor_asprintf(&vote_line, "%s %s %s %s\n",
+ commit_ns_str,
+ crypto_digest_algorithm_get_name(commit->alg),
+ commit->rsa_identity_fpr,
+ commit->encoded_commit);
+ break;
+ case SR_PHASE_REVEAL:
+ {
+ /* Send a reveal value for this commit if we have one. */
+ const char *reveal_str = commit->encoded_reveal;
+ if (tor_mem_is_zero(commit->encoded_reveal,
+ sizeof(commit->encoded_reveal))) {
+ reveal_str = "";
+ }
+ tor_asprintf(&vote_line, "%s %s %s %s %s\n",
+ commit_ns_str,
+ crypto_digest_algorithm_get_name(commit->alg),
+ commit->rsa_identity_fpr,
+ commit->encoded_commit, reveal_str);
+ break;
+ }
+ default:
+ tor_assert(0);
+ }
+
+ log_debug(LD_DIR, "SR: Commit vote line: %s", vote_line);
+ return vote_line;
+}
+
+/* Return a heap allocated string that contains the given <b>srv</b> string
+ * representation formatted for a networkstatus document using the
+ * <b>key</b> as the start of the line. This doesn't return NULL. */
+static char *
+srv_to_ns_string(const sr_srv_t *srv, const char *key)
+{
+ char *srv_str;
+ char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+ tor_assert(srv);
+ tor_assert(key);
+
+ sr_srv_encode(srv_hash_encoded, srv);
+ tor_asprintf(&srv_str, "%s %d %s\n", key,
+ srv->num_reveals, srv_hash_encoded);
+ log_debug(LD_DIR, "SR: Consensus SRV line: %s", srv_str);
+ return srv_str;
+}
+
+/* Given the previous SRV and the current SRV, return a heap allocated
+ * string with their data that could be put in a vote or a consensus. Caller
+ * must free the returned string. Return NULL if no SRVs were provided. */
+static char *
+get_ns_str_from_sr_values(const sr_srv_t *prev_srv, const sr_srv_t *cur_srv)
+{
+ smartlist_t *chunks = NULL;
+ char *srv_str;
+
+ if (!prev_srv && !cur_srv) {
+ return NULL;
+ }
+
+ chunks = smartlist_new();
+
+ if (prev_srv) {
+ char *srv_line = srv_to_ns_string(prev_srv, previous_srv_str);
+ smartlist_add(chunks, srv_line);
+ }
+
+ if (cur_srv) {
+ char *srv_line = srv_to_ns_string(cur_srv, current_srv_str);
+ smartlist_add(chunks, srv_line);
+ }
+
+ /* Join the line(s) here in one string to return. */
+ srv_str = smartlist_join_strings(chunks, "", 0, NULL);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+
+ return srv_str;
+}
+
+/* Return the number of required participants of the SR protocol. This is
+ * based on a consensus params. */
+static int
+get_n_voters_for_srv_agreement(void)
+{
+ int num_dirauths = get_n_authorities(V3_DIRINFO);
+ /* If the params is not found, default value should always be the maximum
+ * number of trusted authorities. Let's not take any chances. */
+ return networkstatus_get_param(NULL, "AuthDirNumSRVAgreements",
+ num_dirauths, 1, num_dirauths);
+}
+
+/* Return 1 if we should we keep an SRV voted by <b>n_agreements</b> auths.
+ * Return 0 if we should ignore it. */
+static int
+should_keep_srv(int n_agreements)
+{
+ /* Check if the most popular SRV has reached majority. */
+ int n_voters = get_n_authorities(V3_DIRINFO);
+ int votes_required_for_majority = (n_voters / 2) + 1;
+
+ /* We need at the very least majority to keep a value. */
+ if (n_agreements < votes_required_for_majority) {
+ log_notice(LD_DIR, "SR: SRV didn't reach majority [%d/%d]!",
+ n_agreements, votes_required_for_majority);
+ return 0;
+ }
+
+ /* When we just computed a new SRV, we need to have super majority in order
+ * to keep it. */
+ if (sr_state_srv_is_fresh()) {
+ /* Check if we have super majority for this new SRV value. */
+ int num_required_agreements = get_n_voters_for_srv_agreement();
+
+ if (n_agreements < num_required_agreements) {
+ log_notice(LD_DIR, "SR: New SRV didn't reach agreement [%d/%d]!",
+ n_agreements, num_required_agreements);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/* Helper: compare two DIGEST256_LEN digests. */
+static int
+compare_srvs_(const void **_a, const void **_b)
+{
+ const sr_srv_t *a = *_a, *b = *_b;
+ return tor_memcmp(a->value, b->value, sizeof(a->value));
+}
+
+/* Return the most frequent member of the sorted list of DIGEST256_LEN
+ * digests in <b>sl</b> with the count of that most frequent element. */
+static sr_srv_t *
+smartlist_get_most_frequent_srv(const smartlist_t *sl, int *count_out)
+{
+ return smartlist_get_most_frequent_(sl, compare_srvs_, count_out);
+}
+
+/* Using a list of <b>votes</b>, return the SRV object from them that has
+ * been voted by the majority of dirauths. If <b>current</b> is set, we look
+ * for the current SRV value else the previous one. The returned pointer is
+ * an object located inside a vote. NULL is returned if no appropriate value
+ * could be found. */
+STATIC sr_srv_t *
+get_majority_srv_from_votes(const smartlist_t *votes, int current)
+{
+ int count = 0;
+ sr_srv_t *most_frequent_srv = NULL;
+ sr_srv_t *the_srv = NULL;
+ smartlist_t *srv_list;
+
+ tor_assert(votes);
+
+ srv_list = smartlist_new();
+
+ /* Walk over votes and register any SRVs found. */
+ SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
+ sr_srv_t *srv_tmp = NULL;
+
+ if (!v->sr_info.participate) {
+ /* Ignore vote that do not participate. */
+ continue;
+ }
+ /* Do we want previous or current SRV? */
+ srv_tmp = current ? v->sr_info.current_srv : v->sr_info.previous_srv;
+ if (!srv_tmp) {
+ continue;
+ }
+
+ smartlist_add(srv_list, srv_tmp);
+ } SMARTLIST_FOREACH_END(v);
+
+ most_frequent_srv = smartlist_get_most_frequent_srv(srv_list, &count);
+ if (!most_frequent_srv) {
+ goto end;
+ }
+
+ /* Was this SRV voted by enough auths for us to keep it? */
+ if (!should_keep_srv(count)) {
+ goto end;
+ }
+
+ /* We found an SRV that we can use! Habemus SRV! */
+ the_srv = most_frequent_srv;
+
+ {
+ /* Debugging */
+ char encoded[SR_SRV_VALUE_BASE64_LEN + 1];
+ sr_srv_encode(encoded, the_srv);
+ log_debug(LD_DIR, "SR: Chosen SRV by majority: %s (%d votes)", encoded,
+ count);
+ }
+
+ end:
+ /* We do not free any sr_srv_t values, we don't have the ownership. */
+ smartlist_free(srv_list);
+ return the_srv;
+}
+
/* Encode the given shared random value and put it in dst. Destination
* buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
void
@@ -572,6 +789,90 @@ sr_parse_commit(const smartlist_t *args)
return NULL;
}
+/* Return a heap-allocated string containing commits that should be put in
+ * the votes. It's the responsibility of the caller to free the string.
+ * This always return a valid string, either empty or with line(s). */
+char *
+sr_get_string_for_vote(void)
+{
+ char *vote_str = NULL;
+ digestmap_t *state_commits;
+ smartlist_t *chunks = smartlist_new();
+ const or_options_t *options = get_options();
+
+ /* Are we participating in the protocol? */
+ if (!options->AuthDirSharedRandomness) {
+ goto end;
+ }
+
+ log_debug(LD_DIR, "SR: Preparing our vote info:");
+
+ /* First line, put in the vote the participation flag. */
+ {
+ char *sr_flag_line;
+ tor_asprintf(&sr_flag_line, "%s\n", sr_flag_ns_str);
+ smartlist_add(chunks, sr_flag_line);
+ }
+
+ /* In our vote we include every commitment in our permanent state. */
+ state_commits = sr_state_get_commits();
+ 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);
+ } DIGESTMAP_FOREACH_END;
+
+ /* Add the SRV value(s) if any. */
+ {
+ char *srv_lines = get_ns_str_from_sr_values(sr_state_get_previous_srv(),
+ sr_state_get_current_srv());
+ if (srv_lines) {
+ smartlist_add(chunks, srv_lines);
+ }
+ }
+
+ end:
+ vote_str = smartlist_join_strings(chunks, "", 0, NULL);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ return vote_str;
+}
+
+/* Return a heap-allocated string that should be put in the consensus and
+ * contains the shared randomness values. It's the responsibility of the
+ * caller to free the string. NULL is returned if no SRV(s) available.
+ *
+ * This is called when a consensus (any flavor) is bring created thus it
+ * should NEVER change the state nor the state should be changed in between
+ * consensus creation. */
+char *
+sr_get_string_for_consensus(const smartlist_t *votes)
+{
+ char *srv_str;
+ const or_options_t *options = get_options();
+
+ tor_assert(votes);
+
+ /* Not participating, avoid returning anything. */
+ if (!options->AuthDirSharedRandomness) {
+ log_info(LD_DIR, "SR: Support disabled (AuthDirSharedRandomness %d)",
+ options->AuthDirSharedRandomness);
+ goto end;
+ }
+
+ /* Check the votes and figure out if SRVs should be included in the final
+ * consensus. */
+ sr_srv_t *prev_srv = get_majority_srv_from_votes(votes, 0);
+ sr_srv_t *cur_srv = get_majority_srv_from_votes(votes, 1);
+ srv_str = get_ns_str_from_sr_values(prev_srv, cur_srv);
+ if (!srv_str) {
+ goto end;
+ }
+
+ return srv_str;
+ end:
+ return NULL;
+}
+
/* Initialize shared random subsystem. This MUST be called early in the boot
* process of tor. Return 0 on success else -1 on error. */
int
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index 878bfbf..f1fbe27 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -101,13 +101,15 @@ typedef struct sr_commit_t {
int sr_init(int save_to_disk);
void sr_save_and_cleanup(void);
+sr_commit_t *sr_parse_commit(const smartlist_t *args);
+sr_srv_t *sr_parse_srv(const smartlist_t *args);
+char *sr_get_string_for_vote(void);
+char *sr_get_string_for_consensus(const smartlist_t *votes);
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);
@@ -123,6 +125,9 @@ STATIC int reveal_decode(const char *encoded, sr_commit_t *commit);
STATIC int commit_has_reveal_value(const sr_commit_t *commit);
+STATIC sr_srv_t *get_majority_srv_from_votes(const smartlist_t *votes,
+ int current);
+
#endif /* SHARED_RANDOM_PRIVATE */
#endif /* TOR_SHARED_RANDOM_H */
1
0

[tor/master] prop250: Add version to Commit line in vote and state
by nickm@torproject.org 01 Jul '16
by nickm@torproject.org 01 Jul '16
01 Jul '16
commit 0f27d92e4c420cae8fff0ab889ad8494461d6f8e
Author: David Goulet <dgoulet(a)torproject.org>
Date: Wed May 11 10:21:34 2016 -0400
prop250: Add version to Commit line in vote and state
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 38 ++++++++++++++++++++++++++------------
src/or/shared_random_state.c | 3 ++-
2 files changed, 28 insertions(+), 13 deletions(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index e56a239..6e6ff3b 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -97,6 +97,7 @@
#include "router.h"
#include "routerlist.h"
#include "shared_random_state.h"
+#include "util.h"
/* String prefix of shared random values in votes/consensuses. */
static const char previous_srv_str[] = "shared-rand-previous-value";
@@ -461,8 +462,9 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
switch (phase) {
case SR_PHASE_COMMIT:
- tor_asprintf(&vote_line, "%s %s %s %s\n",
+ tor_asprintf(&vote_line, "%s %u %s %s %s\n",
commit_ns_str,
+ SR_PROTO_VERSION,
crypto_digest_algorithm_get_name(commit->alg),
sr_commit_get_rsa_fpr(commit),
commit->encoded_commit);
@@ -475,8 +477,9 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
sizeof(commit->encoded_reveal))) {
reveal_str = "";
}
- tor_asprintf(&vote_line, "%s %s %s %s %s\n",
+ tor_asprintf(&vote_line, "%s %u %s %s %s %s\n",
commit_ns_str,
+ SR_PROTO_VERSION,
crypto_digest_algorithm_get_name(commit->alg),
sr_commit_get_rsa_fpr(commit),
commit->encoded_commit, reveal_str);
@@ -1040,22 +1043,33 @@ sr_parse_srv(const smartlist_t *args)
* 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]
+ * version, algname, RSA fingerprint, commit value[, reveal value]
*/
sr_commit_t *
sr_parse_commit(const smartlist_t *args)
{
+ uint32_t version;
char *value, digest[DIGEST_LEN];
digest_algorithm_t alg;
const char *rsa_identity_fpr;
sr_commit_t *commit = NULL;
- if (smartlist_len(args) < 3) {
+ if (smartlist_len(args) < 4) {
goto error;
}
- /* First argument is the algorithm. */
+ /* First is the version number of the SR protocol which indicates at which
+ * version that commit was created. */
value = smartlist_get(args, 0);
+ version = (uint32_t) tor_parse_ulong(value, 10, 1, UINT32_MAX, NULL, NULL);
+ if (version > SR_PROTO_VERSION) {
+ log_info(LD_DIR, "SR: Commit version %" PRIu32 " (%s) is not supported.",
+ version, escaped(value));
+ goto error;
+ }
+
+ /* Second is the algorithm. */
+ value = smartlist_get(args, 1);
alg = crypto_digest_algorithm_parse_name(value);
if (alg != SR_DIGEST_ALG) {
log_warn(LD_BUG, "SR: Commit algorithm %s is not recognized.",
@@ -1063,9 +1077,9 @@ sr_parse_commit(const smartlist_t *args)
goto error;
}
- /* Second argument is the RSA fingerprint of the auth and turn it into a
+ /* Third argument is the RSA fingerprint of the auth and turn it into a
* digest value. */
- rsa_identity_fpr = smartlist_get(args, 1);
+ 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",
@@ -1085,15 +1099,15 @@ sr_parse_commit(const smartlist_t *args)
/* Allocate commit since we have a valid identity now. */
commit = commit_new(digest);
- /* Third argument is the commitment value base64-encoded. */
- value = smartlist_get(args, 2);
+ /* Fourth argument is the commitment value base64-encoded. */
+ value = smartlist_get(args, 3);
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);
+ /* (Optional) Fifth argument is the revealed value. */
+ if (smartlist_len(args) > 4) {
+ value = smartlist_get(args, 4);
if (reveal_decode(value, commit) < 0) {
goto error;
}
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 115d954..9700bf5 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -563,7 +563,8 @@ disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line)
/* 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",
+ tor_asprintf(&line->value, "%u %s %s %s%s",
+ SR_PROTO_VERSION,
crypto_digest_algorithm_get_name(commit->alg),
sr_commit_get_rsa_fpr(commit),
commit->encoded_commit,
1
0

[tor/master] prop250: Don't use {0} to init static struct -- causes warning on clang.
by nickm@torproject.org 01 Jul '16
by nickm@torproject.org 01 Jul '16
01 Jul '16
commit 899d2b890b35b9772f33054b9ff627dd8186deac
Author: George Kadianakis <desnacked(a)riseup.net>
Date: Wed May 25 12:28:40 2016 +0300
prop250: Don't use {0} to init static struct -- causes warning on clang.
See ticket #19132 for the clang/llvm warning.
Since voting_schedule is a global static struct, it will be initialized
to zero even without explicitly initializing it with {0}.
This is what the C spec says:
If an object that has automatic storage duration is not initialized
explicitly, its value is indeterminate. If an object that has static
storage duration is not initialized explicitly, then:
— if it has pointer type, it is initialized to a null pointer;
— if it has arithmetic type, it is initialized to (positive or unsigned) zero;
— if it is an aggregate, every member is initialized (recursively) according to these rules;
— if it is a union, the first named member is initialized (recursively) according to these rules.
---
src/or/dirvote.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/or/dirvote.c b/src/or/dirvote.c
index 4eccdd0..5071331 100644
--- a/src/or/dirvote.c
+++ b/src/or/dirvote.c
@@ -2545,7 +2545,7 @@ get_next_valid_after_time(time_t now)
return next_valid_after_time;
}
-static voting_schedule_t voting_schedule = {0};
+static voting_schedule_t voting_schedule;
/** Set voting_schedule to hold the timing for the next vote we should be
* doing. */
1
0

[tor/master] prop250: Don't reject votes containing commits of unknown dirauths.
by nickm@torproject.org 01 Jul '16
by nickm@torproject.org 01 Jul '16
01 Jul '16
commit f6f4668b1d0dec1a9afc25ef9d7fcd884cc55608
Author: George Kadianakis <desnacked(a)riseup.net>
Date: Tue May 24 13:56:39 2016 +0300
prop250: Don't reject votes containing commits of unknown dirauths.
Instead just ignore those commits.
Squash this commit with 33b2ade.
---
src/or/shared_random.c | 19 ++++++++++---------
src/test/test_shared_random.c | 8 ++++----
2 files changed, 14 insertions(+), 13 deletions(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index 8427b68..7da6daf 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -599,6 +599,16 @@ should_keep_commit(const sr_commit_t *commit, const char *voter_key,
goto ignore;
}
+ /* Let's make sure, for extra safety, that this fingerprint is known to
+ * us. Even though this comes from a vote, doesn't hurt to be
+ * extracareful. */
+ if (trusteddirserver_get_by_v3_auth_digest(commit->rsa_identity) == NULL) {
+ log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized "
+ "authority. Discarding commit.",
+ escaped(commit->rsa_identity));
+ goto ignore;
+ }
+
/* Check if the authority that voted for <b>commit</b> has already posted
* a commit before. */
saved_commit = sr_state_get_commit(commit->rsa_identity);
@@ -1108,15 +1118,6 @@ sr_parse_commit(const smartlist_t *args)
escaped(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.",
- escaped(rsa_identity_fpr));
- goto error;
- }
/* Allocate commit since we have a valid identity now. */
commit = commit_new(digest);
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 18d45b5..dcd71e0 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -277,9 +277,6 @@ test_sr_commit(void *arg)
(void) arg;
- MOCK(trusteddirserver_get_by_v3_auth_digest,
- trusteddirserver_get_by_v3_auth_digest_m);
-
{ /* Setup a minimal dirauth environment for this test */
or_options_t *options = get_options_mutable();
@@ -366,7 +363,6 @@ test_sr_commit(void *arg)
done:
smartlist_free(args);
sr_commit_free(our_commit);
- UNMOCK(trusteddirserver_get_by_v3_auth_digest);
}
/* Test the encoding and decoding function for commit and reveal values. */
@@ -1121,6 +1117,9 @@ test_keep_commit(void *arg)
(void) arg;
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
{ /* Setup a minimal dirauth environment for this test */
crypto_pk_t *k = crypto_pk_new();
/* Have a key that is not the one from our commit. */
@@ -1199,6 +1198,7 @@ test_keep_commit(void *arg)
done:
sr_commit_free(commit);
sr_commit_free(dup_commit);
+ UNMOCK(trusteddirserver_get_by_v3_auth_digest);
}
static void
1
0

[tor/master] prop250: Use RSA identity digest instead of fingerprint
by nickm@torproject.org 01 Jul '16
by nickm@torproject.org 01 Jul '16
01 Jul '16
commit 056b6186adeb5ee92d0899f60b5e061bfc11a8ba
Author: David Goulet <dgoulet(a)ev0ke.net>
Date: Mon May 9 18:58:19 2016 -0400
prop250: Use RSA identity digest instead of fingerprint
The prop250 code used the RSA identity key fingerprint to index commit in a
digestmap instead of using the digest.
To behavior change except the fact that we are actually using digestmap
correctly.
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
---
src/or/shared_random.c | 68 +++++++++++++++++++++++--------------------
src/or/shared_random.h | 11 +++++--
src/or/shared_random_state.c | 18 ++++++------
src/test/test_shared_random.c | 56 ++++++++++++++++-------------------
4 files changed, 78 insertions(+), 75 deletions(-)
diff --git a/src/or/shared_random.c b/src/or/shared_random.c
index f5f4ccf..967e1e1 100644
--- a/src/or/shared_random.c
+++ b/src/or/shared_random.c
@@ -120,20 +120,19 @@ srv_dup(const sr_srv_t *orig)
return dup;
}
-/* Allocate a new commit object and initializing it with <b>identity</b>
+/* Allocate a new commit object and initializing it with <b>rsa_identity</b>
* that MUST be provided. The digest algorithm is set to the default one
* that is supported. The rest is uninitialized. This never returns NULL. */
static sr_commit_t *
-commit_new(const char *rsa_identity_fpr)
+commit_new(const char *rsa_identity)
{
sr_commit_t *commit;
- tor_assert(rsa_identity_fpr);
+ tor_assert(rsa_identity);
commit = tor_malloc_zero(sizeof(*commit));
commit->alg = SR_DIGEST_ALG;
- strlcpy(commit->rsa_identity_fpr, rsa_identity_fpr,
- sizeof(commit->rsa_identity_fpr));
+ memcpy(commit->rsa_identity, rsa_identity, sizeof(commit->rsa_identity));
return commit;
}
@@ -143,7 +142,7 @@ commit_log(const sr_commit_t *commit)
{
tor_assert(commit);
- log_debug(LD_DIR, "SR: Commit from %s", commit->rsa_identity_fpr);
+ log_debug(LD_DIR, "SR: Commit from %s", sr_commit_get_rsa_fpr(commit));
log_debug(LD_DIR, "SR: Commit: [TS: %" PRIu64 "] [Encoded: %s]",
commit->commit_ts, commit->encoded_commit);
log_debug(LD_DIR, "SR: Reveal: [TS: %" PRIu64 "] [Encoded: %s]",
@@ -160,7 +159,7 @@ verify_commit_and_reveal(const sr_commit_t *commit)
tor_assert(commit);
log_debug(LD_DIR, "SR: Validating commit from authority %s",
- commit->rsa_identity_fpr);
+ sr_commit_get_rsa_fpr(commit));
/* Check that the timestamps match. */
if (commit->commit_ts != commit->reveal_ts) {
@@ -194,7 +193,7 @@ verify_commit_and_reveal(const sr_commit_t *commit)
sizeof(received_hashed_reveal))) {
log_warn(LD_BUG, "SR: Received reveal value from authority %s "
"does't match the commit value.",
- commit->rsa_identity_fpr);
+ sr_commit_get_rsa_fpr(commit));
goto invalid;
}
}
@@ -242,14 +241,14 @@ commit_decode(const char *encoded, sr_commit_t *commit)
encoded, strlen(encoded));
if (decoded_len < 0) {
log_warn(LD_BUG, "SR: Commit from authority %s can't be decoded.",
- commit->rsa_identity_fpr);
+ sr_commit_get_rsa_fpr(commit));
goto error;
}
if (decoded_len != SR_COMMIT_LEN) {
log_warn(LD_BUG, "SR: Commit from authority %s decoded length doesn't "
"match the expected length (%d vs %d).",
- commit->rsa_identity_fpr, decoded_len, SR_COMMIT_LEN);
+ sr_commit_get_rsa_fpr(commit), decoded_len, SR_COMMIT_LEN);
goto error;
}
@@ -295,14 +294,14 @@ reveal_decode(const char *encoded, sr_commit_t *commit)
encoded, strlen(encoded));
if (decoded_len < 0) {
log_warn(LD_BUG, "SR: Reveal from authority %s can't be decoded.",
- commit->rsa_identity_fpr);
+ sr_commit_get_rsa_fpr(commit));
goto error;
}
if (decoded_len != SR_REVEAL_LEN) {
log_warn(LD_BUG, "SR: Reveal from authority %s decoded length is "
"doesn't match the expected length (%d vs %d)",
- commit->rsa_identity_fpr, decoded_len, SR_REVEAL_LEN);
+ sr_commit_get_rsa_fpr(commit), decoded_len, SR_REVEAL_LEN);
goto error;
}
@@ -396,7 +395,7 @@ get_srv_element_from_commit(const sr_commit_t *commit)
return NULL;
}
- tor_asprintf(&element, "%s%s", commit->rsa_identity_fpr,
+ tor_asprintf(&element, "%s%s", sr_commit_get_rsa_fpr(commit),
commit->encoded_reveal);
return element;
}
@@ -465,7 +464,7 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
tor_asprintf(&vote_line, "%s %s %s %s\n",
commit_ns_str,
crypto_digest_algorithm_get_name(commit->alg),
- commit->rsa_identity_fpr,
+ sr_commit_get_rsa_fpr(commit),
commit->encoded_commit);
break;
case SR_PHASE_REVEAL:
@@ -479,7 +478,7 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
tor_asprintf(&vote_line, "%s %s %s %s %s\n",
commit_ns_str,
crypto_digest_algorithm_get_name(commit->alg),
- commit->rsa_identity_fpr,
+ sr_commit_get_rsa_fpr(commit),
commit->encoded_commit, reveal_str);
break;
}
@@ -567,7 +566,8 @@ commit_is_authoritative(const sr_commit_t *commit,
tor_assert(commit);
tor_assert(voter_key);
- return !strcmp(commit->rsa_identity_fpr, voter_key);
+ return !memcmp(commit->rsa_identity, voter_key,
+ sizeof(commit->rsa_identity));
}
/* Decide if the newly received <b>commit</b> should be kept depending on
@@ -586,7 +586,8 @@ should_keep_commit(const sr_commit_t *commit, const char *voter_key,
tor_assert(voter_key);
log_debug(LD_DIR, "SR: Inspecting commit from %s (voter: %s)?",
- commit->rsa_identity_fpr, voter_key);
+ sr_commit_get_rsa_fpr(commit),
+ hex_str(voter_key, DIGEST_LEN));
/* For a commit to be considered, it needs to be authoritative (it should
* be the voter's own commit). */
@@ -597,7 +598,7 @@ should_keep_commit(const sr_commit_t *commit, const char *voter_key,
/* Check if the authority that voted for <b>commit</b> has already posted
* a commit before. */
- saved_commit = sr_state_get_commit(commit->rsa_identity_fpr);
+ saved_commit = sr_state_get_commit(commit->rsa_identity);
switch (phase) {
case SR_PHASE_COMMIT:
@@ -611,7 +612,8 @@ should_keep_commit(const sr_commit_t *commit, const char *voter_key,
if (commit_has_reveal_value(commit)) {
log_warn(LD_DIR, "SR: Commit from authority %s has a reveal value "
"during COMMIT phase. (voter: %s)",
- commit->rsa_identity_fpr, voter_key);
+ sr_commit_get_rsa_fpr(commit),
+ hex_str(voter_key, DIGEST_LEN));
goto ignore;
}
break;
@@ -635,7 +637,8 @@ should_keep_commit(const sr_commit_t *commit, const char *voter_key,
if (!commitments_are_the_same(commit, saved_commit)) {
log_warn(LD_DIR, "SR: Commit from authority %s is different from "
"previous commit in our state (voter: %s)",
- commit->rsa_identity_fpr, voter_key);
+ sr_commit_get_rsa_fpr(commit),
+ hex_str(voter_key, DIGEST_LEN));
goto ignore;
}
@@ -652,7 +655,8 @@ should_keep_commit(const sr_commit_t *commit, const char *voter_key,
if (verify_commit_and_reveal(commit) < 0) {
log_warn(LD_BUG, "SR: Commit from authority %s has an invalid "
"reveal value. (voter: %s)",
- commit->rsa_identity_fpr, voter_key);
+ sr_commit_get_rsa_fpr(commit),
+ hex_str(voter_key, DIGEST_LEN));
goto ignore;
}
break;
@@ -677,7 +681,7 @@ save_commit_during_reveal_phase(const sr_commit_t *commit)
tor_assert(commit);
/* Get the commit from our state. */
- saved_commit = sr_state_get_commit(commit->rsa_identity_fpr);
+ saved_commit = sr_state_get_commit(commit->rsa_identity);
tor_assert(saved_commit);
/* Safety net. They can not be different commitments at this point. */
int same_commits = commitments_are_the_same(commit, saved_commit);
@@ -868,18 +872,17 @@ 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];
+ char digest[DIGEST_LEN];
tor_assert(my_rsa_cert);
/* Get our RSA identity fingerprint */
- if (crypto_pk_get_fingerprint(my_rsa_cert->identity_key,
- fingerprint, 0) < 0) {
+ if (crypto_pk_get_digest(my_rsa_cert->identity_key, digest) < 0) {
goto error;
}
/* New commit with our identity key. */
- commit = commit_new(fingerprint);
+ commit = commit_new(digest);
/* Generate the reveal random value */
crypto_strongest_rand(commit->random_number,
@@ -1036,7 +1039,7 @@ sr_parse_srv(const smartlist_t *args)
sr_commit_t *
sr_parse_commit(const smartlist_t *args)
{
- char *value;
+ char *value, digest[DIGEST_LEN];
digest_algorithm_t alg;
const char *rsa_identity_fpr;
sr_commit_t *commit = NULL;
@@ -1054,7 +1057,8 @@ sr_parse_commit(const smartlist_t *args)
goto error;
}
- /* Second argument is the RSA fingerprint of the auth */
+ /* Second argument is the RSA fingerprint of the auth and turn it into a
+ * digest value. */
rsa_identity_fpr = smartlist_get(args, 1);
if (base16_decode(digest, DIGEST_LEN, rsa_identity_fpr,
HEX_DIGEST_LEN) < 0) {
@@ -1073,7 +1077,7 @@ sr_parse_commit(const smartlist_t *args)
}
/* Allocate commit since we have a valid identity now. */
- commit = commit_new(rsa_identity_fpr);
+ commit = commit_new(digest);
/* Third argument is the commitment value base64-encoded. */
value = smartlist_get(args, 2);
@@ -1103,7 +1107,7 @@ sr_parse_commit(const smartlist_t *args)
void
sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key)
{
- char rsa_identity_fpr[FINGERPRINT_LEN + 1];
+ char rsa_identity[DIGEST_LEN];
tor_assert(voter_key);
@@ -1113,7 +1117,7 @@ sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key)
}
/* Get the RSA identity fingerprint of this voter */
- if (crypto_pk_get_fingerprint(voter_key, rsa_identity_fpr, 0) < 0) {
+ if (crypto_pk_get_digest(voter_key, rsa_identity) < 0) {
return;
}
@@ -1121,7 +1125,7 @@ sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key)
/* We won't need the commit in this list anymore, kept or not. */
SMARTLIST_DEL_CURRENT(commits, commit);
/* Check if this commit is valid and should be stored in our state. */
- if (!should_keep_commit(commit, rsa_identity_fpr,
+ if (!should_keep_commit(commit, rsa_identity,
sr_state_get_phase())) {
sr_commit_free(commit);
continue;
diff --git a/src/or/shared_random.h b/src/or/shared_random.h
index 15eed8a..573c499 100644
--- a/src/or/shared_random.h
+++ b/src/or/shared_random.h
@@ -71,8 +71,8 @@ typedef struct sr_commit_t {
/* Commit owner info */
- /* The RSA identity fingerprint of the authority. */
- char rsa_identity_fpr[FINGERPRINT_LEN + 1];
+ /* The RSA identity key of the authority. */
+ char rsa_identity[DIGEST_LEN];
/* Commitment information */
@@ -112,11 +112,16 @@ 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): */
+static inline
+const char *sr_commit_get_rsa_fpr(const sr_commit_t *commit)
+{
+ return hex_str((const char *) commit->rsa_identity,
+ sizeof(commit->rsa_identity));
+}
void sr_compute_srv(void);
sr_commit_t *sr_generate_our_commit(time_t timestamp,
const authority_cert_t *my_rsa_cert);
-
#ifdef SHARED_RANDOM_PRIVATE
/* Encode */
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 7c75431..326b8c9 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -218,14 +218,14 @@ commit_add_to_state(sr_commit_t *commit, sr_state_t *state)
tor_assert(commit);
tor_assert(state);
- saved_commit = digestmap_set(state->commits, commit->rsa_identity_fpr,
+ saved_commit = digestmap_set(state->commits, commit->rsa_identity,
commit);
if (saved_commit != NULL) {
/* This means we already have that commit in our state so adding twice
* the same commit is either a code flow error, a corrupted disk state
* or some new unknown issue. */
log_warn(LD_DIR, "SR: Commit from %s exists in our state while "
- "adding it: '%s'", commit->rsa_identity_fpr,
+ "adding it: '%s'", sr_commit_get_rsa_fpr(commit),
commit->encoded_commit);
sr_commit_free(saved_commit);
}
@@ -562,7 +562,7 @@ disk_state_put_commit_line(const sr_commit_t *commit, config_line_t *line)
}
tor_asprintf(&line->value, "%s %s %s%s",
crypto_digest_algorithm_get_name(commit->alg),
- commit->rsa_identity_fpr,
+ sr_commit_get_rsa_fpr(commit),
commit->encoded_commit,
reveal_str != NULL ? reveal_str : "");
if (reveal_str != NULL) {
@@ -1124,17 +1124,17 @@ sr_state_update(time_t valid_after)
}
}
-/* Return commit object from the given authority digest <b>identity</b>.
+/* Return commit object from the given authority digest <b>rsa_identity</b>.
* Return NULL if not found. */
sr_commit_t *
-sr_state_get_commit(const char *rsa_fpr)
+sr_state_get_commit(const char *rsa_identity)
{
sr_commit_t *commit;
- tor_assert(rsa_fpr);
+ tor_assert(rsa_identity);
state_query(SR_STATE_ACTION_GET, SR_STATE_OBJ_COMMIT,
- (void *) rsa_fpr, (void *) &commit);
+ (void *) rsa_identity, (void *) &commit);
return commit;
}
@@ -1150,7 +1150,7 @@ sr_state_add_commit(sr_commit_t *commit)
(void *) commit, NULL);
log_debug(LD_DIR, "SR: Commit from %s has been added to our state.",
- commit->rsa_identity_fpr);
+ sr_commit_get_rsa_fpr(commit));
}
/* Remove all commits from our state. */
@@ -1178,7 +1178,7 @@ sr_state_copy_reveal_info(sr_commit_t *saved_commit, const sr_commit_t *commit)
state_query(SR_STATE_ACTION_SAVE, 0, NULL, NULL);
log_debug(LD_DIR, "SR: Reveal value learned %s (for commit %s) from %s",
saved_commit->encoded_reveal, saved_commit->encoded_commit,
- saved_commit->rsa_identity_fpr);
+ sr_commit_get_rsa_fpr(saved_commit));
}
/* Set the fresh SRV flag from our state. This doesn't need to trigger a
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
index 1125717..fbb4414 100644
--- a/src/test/test_shared_random.c
+++ b/src/test/test_shared_random.c
@@ -331,7 +331,7 @@ test_sr_commit(void *arg)
sr_commit_t *parsed_commit;
smartlist_add(args,
tor_strdup(crypto_digest_algorithm_get_name(our_commit->alg)));
- smartlist_add(args, our_commit->rsa_identity_fpr);
+ smartlist_add(args, tor_strdup(sr_commit_get_rsa_fpr(our_commit)));
smartlist_add(args, our_commit->encoded_commit);
smartlist_add(args, our_commit->encoded_reveal);
parsed_commit = sr_parse_commit(args);
@@ -480,7 +480,7 @@ test_vote(void *arg)
tt_assert(our_commit);
sr_state_add_commit(our_commit);
/* Make sure it's there. */
- saved_commit = sr_state_get_commit(our_commit->rsa_identity_fpr);
+ saved_commit = sr_state_get_commit(our_commit->rsa_identity);
tt_assert(saved_commit);
}
@@ -508,8 +508,10 @@ test_vote(void *arg)
tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-commit");
tt_str_op(smartlist_get(tokens, 1), OP_EQ,
crypto_digest_algorithm_get_name(DIGEST_SHA3_256));
- tt_str_op(smartlist_get(tokens, 2), OP_EQ,
- our_commit->rsa_identity_fpr);
+ char digest[DIGEST_LEN];
+ base16_decode(digest, sizeof(digest), smartlist_get(tokens, 2),
+ HEX_DIGEST_LEN);
+ tt_mem_op(digest, ==, our_commit->rsa_identity, sizeof(digest));
tt_str_op(smartlist_get(tokens, 3), OP_EQ, our_commit->encoded_commit);
tt_str_op(smartlist_get(tokens, 4), OP_EQ, our_commit->encoded_reveal);
@@ -664,9 +666,7 @@ test_sr_setup_commits(void)
tt_assert(commit_a);
/* Do some surgery on the commit */
- strlcpy(commit_a->rsa_identity_fpr,
- "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
- sizeof(commit_a->rsa_identity_fpr));
+ memset(commit_a->rsa_identity, 'A', sizeof(commit_a->rsa_identity));
strlcpy(commit_a->encoded_reveal,
"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
sizeof(commit_a->encoded_reveal));
@@ -680,9 +680,7 @@ test_sr_setup_commits(void)
tt_assert(commit_b);
/* Do some surgery on the commit */
- strlcpy(commit_b->rsa_identity_fpr,
- "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
- sizeof(commit_b->rsa_identity_fpr));
+ memset(commit_b->rsa_identity, 'B', sizeof(commit_b->rsa_identity));
strlcpy(commit_b->encoded_reveal,
"BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
sizeof(commit_b->encoded_reveal));
@@ -696,9 +694,7 @@ test_sr_setup_commits(void)
tt_assert(commit_c);
/* Do some surgery on the commit */
- strlcpy(commit_c->rsa_identity_fpr,
- "ccccccccccccccccccccccccccccccccccccccccccccccccc",
- sizeof(commit_c->rsa_identity_fpr));
+ memset(commit_c->rsa_identity, 'C', sizeof(commit_c->rsa_identity));
strlcpy(commit_c->encoded_reveal,
"CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
sizeof(commit_c->encoded_reveal));
@@ -712,9 +708,7 @@ test_sr_setup_commits(void)
tt_assert(commit_d);
/* Do some surgery on the commit */
- strlcpy(commit_d->rsa_identity_fpr,
- "ddddddddddddddddddddddddddddddddddddddddddddddddd",
- sizeof(commit_d->rsa_identity_fpr));
+ memset(commit_d->rsa_identity, 'D', sizeof(commit_d->rsa_identity));
strlcpy(commit_d->encoded_reveal,
"DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
sizeof(commit_d->encoded_reveal));
@@ -946,17 +940,17 @@ test_utils(void *arg)
/* Testing commit_is_authoritative(). */
{
crypto_pk_t *k = crypto_pk_new();
- char fp[FINGERPRINT_LEN + 1];
+ char digest[DIGEST_LEN];
sr_commit_t commit;
tt_assert(!crypto_pk_generate_key(k));
- tt_int_op(0, ==, crypto_pk_get_fingerprint(k, fp, 0));
- memcpy(fp, commit.rsa_identity_fpr, sizeof(fp));
- tt_int_op(commit_is_authoritative(&commit, fp), ==, 1);
+ tt_int_op(0, ==, crypto_pk_get_digest(k, digest));
+ memcpy(commit.rsa_identity, digest, sizeof(commit.rsa_identity));
+ tt_int_op(commit_is_authoritative(&commit, digest), ==, 1);
/* Change the pubkey. */
- memset(commit.rsa_identity_fpr, 0, sizeof(commit.rsa_identity_fpr));
- tt_int_op(commit_is_authoritative(&commit, fp), ==, 0);
+ memset(commit.rsa_identity, 0, sizeof(commit.rsa_identity));
+ tt_int_op(commit_is_authoritative(&commit, digest), ==, 0);
}
/* Testing get_phase_str(). */
@@ -1105,17 +1099,17 @@ test_keep_commit(void *arg)
tt_int_op(should_keep_commit(commit, fp, SR_PHASE_COMMIT), ==, 0);
/* This should NOT be kept because it has a reveal value in it. */
tt_assert(commit_has_reveal_value(commit));
- tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity,
SR_PHASE_COMMIT), ==, 0);
/* Add it to the state which should return to not keep it. */
sr_state_add_commit(commit);
- tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity,
SR_PHASE_COMMIT), ==, 0);
/* Remove it from state so we can continue our testing. */
- digestmap_remove(state->commits, commit->rsa_identity_fpr);
+ digestmap_remove(state->commits, commit->rsa_identity);
/* Let's remove our reveal value which should make it OK to keep it. */
memset(commit->encoded_reveal, 0, sizeof(commit->encoded_reveal));
- tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity,
SR_PHASE_COMMIT), ==, 1);
/* Let's reset our commit and go into REVEAL phase. */
@@ -1130,14 +1124,14 @@ test_keep_commit(void *arg)
/* We should never keep a commit from a non authoritative authority. */
tt_int_op(should_keep_commit(commit, fp, SR_PHASE_REVEAL), ==, 0);
/* We shouldn't accept a commit that is not in our state. */
- tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity,
SR_PHASE_REVEAL), ==, 0);
/* Important to add the commit _without_ the reveal here. */
sr_state_add_commit(dup_commit);
tt_int_op(digestmap_size(state->commits), ==, 1);
/* Our commit should be valid that is authoritative, contains a reveal, be
* in the state and commitment and reveal values match. */
- tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity,
SR_PHASE_REVEAL), ==, 1);
/* The commit shouldn't be kept if it's not verified that is no matchin
* hashed reveal. */
@@ -1147,17 +1141,17 @@ test_keep_commit(void *arg)
memcpy(place_holder.hashed_reveal, commit->hashed_reveal,
sizeof(place_holder.hashed_reveal));
memset(commit->hashed_reveal, 0, sizeof(commit->hashed_reveal));
- tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity,
SR_PHASE_REVEAL), ==, 0);
memcpy(commit->hashed_reveal, place_holder.hashed_reveal,
sizeof(commit->hashed_reveal));
}
/* We shouldn't keep a commit that has no reveal. */
- tt_int_op(should_keep_commit(dup_commit, dup_commit->rsa_identity_fpr,
+ tt_int_op(should_keep_commit(dup_commit, dup_commit->rsa_identity,
SR_PHASE_REVEAL), ==, 0);
/* We must not keep a commit that is not the same from the commit phase. */
memset(commit->encoded_commit, 0, sizeof(commit->encoded_commit));
- tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity,
SR_PHASE_REVEAL), ==, 0);
done:
1
0
commit 39be8af7092a4ee004f4c53fa1e55678d9d67f1f
Author: David Goulet <dgoulet(a)torproject.org>
Date: Tue May 3 11:42:50 2016 -0400
prop250: Add unit tests
Signed-off-by: David Goulet <dgoulet(a)torproject.org>
Signed-off-by: George Kadianakis <desnacked(a)riseup.net>
---
src/or/networkstatus.c | 8 +-
src/or/networkstatus.h | 4 +-
src/or/shared_random_state.c | 19 +
src/or/shared_random_state.h | 7 +
src/test/include.am | 1 +
src/test/sr_srv_calc_ref.py | 71 +++
src/test/test.c | 1 +
src/test/test.h | 1 +
src/test/test_dir.c | 73 ++-
src/test/test_routerlist.c | 17 +
src/test/test_shared_random.c | 1256 +++++++++++++++++++++++++++++++++++++++++
11 files changed, 1449 insertions(+), 9 deletions(-)
diff --git a/src/or/networkstatus.c b/src/or/networkstatus.c
index aabbfff..cf395f9 100644
--- a/src/or/networkstatus.c
+++ b/src/or/networkstatus.c
@@ -1191,8 +1191,8 @@ consensus_is_waiting_for_certs(void)
/** Return the most recent consensus that we have downloaded, or NULL if we
* don't have one. */
-networkstatus_t *
-networkstatus_get_latest_consensus(void)
+MOCK_IMPL(networkstatus_t *,
+networkstatus_get_latest_consensus,(void))
{
return current_consensus;
}
@@ -1214,8 +1214,8 @@ networkstatus_get_latest_consensus_by_flavor,(consensus_flavor_t f))
/** Return the most recent consensus that we have downloaded, or NULL if it is
* no longer live. */
-networkstatus_t *
-networkstatus_get_live_consensus(time_t now)
+MOCK_IMPL(networkstatus_t *,
+networkstatus_get_live_consensus,(time_t now))
{
if (current_consensus &&
current_consensus->valid_after <= now &&
diff --git a/src/or/networkstatus.h b/src/or/networkstatus.h
index aee6641..b34696e 100644
--- a/src/or/networkstatus.h
+++ b/src/or/networkstatus.h
@@ -64,10 +64,10 @@ void update_certificate_downloads(time_t now);
int consensus_is_waiting_for_certs(void);
int client_would_use_router(const routerstatus_t *rs, time_t now,
const or_options_t *options);
-networkstatus_t *networkstatus_get_latest_consensus(void);
+MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus,(void));
MOCK_DECL(networkstatus_t *,networkstatus_get_latest_consensus_by_flavor,
(consensus_flavor_t f));
-networkstatus_t *networkstatus_get_live_consensus(time_t now);
+MOCK_DECL(networkstatus_t *, networkstatus_get_live_consensus,(time_t now));
networkstatus_t *networkstatus_get_reasonably_live_consensus(time_t now,
int flavor);
MOCK_DECL(int, networkstatus_consensus_is_bootstrapping,(time_t now));
diff --git a/src/or/shared_random_state.c b/src/or/shared_random_state.c
index 6dd10d6..7c75431 100644
--- a/src/or/shared_random_state.c
+++ b/src/or/shared_random_state.c
@@ -1291,3 +1291,22 @@ sr_state_init(int save_to_disk, int read_from_disk)
error:
return -1;
}
+
+#ifdef TOR_UNIT_TESTS
+
+/* Set the current phase of the protocol. Used only by unit tests. */
+void
+set_sr_phase(sr_phase_t phase)
+{
+ tor_assert(sr_state);
+ sr_state->phase = phase;
+}
+
+/* Get the SR state. Used only by unit tests */
+sr_state_t *
+get_sr_state(void)
+{
+ return sr_state;
+}
+
+#endif /* TOR_UNIT_TESTS */
diff --git a/src/or/shared_random_state.h b/src/or/shared_random_state.h
index 499a375..d013650 100644
--- a/src/or/shared_random_state.h
+++ b/src/or/shared_random_state.h
@@ -135,4 +135,11 @@ STATIC int is_phase_transition(sr_phase_t next_phase);
#endif /* SHARED_RANDOM_STATE_PRIVATE */
+#ifdef TOR_UNIT_TESTS
+
+STATIC void set_sr_phase(sr_phase_t phase);
+STATIC sr_state_t *get_sr_state(void);
+
+#endif /* TOR_UNIT_TESTS */
+
#endif /* TOR_SHARED_RANDOM_STATE_H */
diff --git a/src/test/include.am b/src/test/include.am
index 5a91c74..d0bc808 100644
--- a/src/test/include.am
+++ b/src/test/include.am
@@ -116,6 +116,7 @@ src_test_test_SOURCES = \
src/test/test_routerlist.c \
src/test/test_routerset.c \
src/test/test_scheduler.c \
+ src/test/test_shared_random.c \
src/test/test_socks.c \
src/test/test_status.c \
src/test/test_threads.c \
diff --git a/src/test/sr_srv_calc_ref.py b/src/test/sr_srv_calc_ref.py
new file mode 100644
index 0000000..492ca62
--- /dev/null
+++ b/src/test/sr_srv_calc_ref.py
@@ -0,0 +1,71 @@
+# This is a reference implementation of the SRV calculation for prop250. We
+# use it to generate a test vector for the test_sr_compute_srv() unittest.
+# (./test shared-random/sr_compute_srv)
+#
+# Here is the SRV computation formula:
+#
+# HASHED_REVEALS = H(ID_a | R_a | ID_b | R_b | ..)
+#
+# SRV = SHA3-256("shared-random" | INT_8(reveal_num) | INT_4(version) |
+# HASHED_REVEALS | previous_SRV)
+#
+
+import sys
+import hashlib
+import struct
+
+# Python 3.6+, the SHA3 is available in hashlib natively. Else this requires
+# the pysha3 package (pip install pysha3).
+if sys.version_info < (3, 6):
+ import sha3
+
+# Test vector to make sure the right sha3 version will be used. pysha3 < 1.0
+# used the old Keccak implementation. During the finalization of SHA3, NIST
+# changed the delimiter suffix from 0x01 to 0x06. The Keccak sponge function
+# stayed the same. pysha3 1.0 provides the previous Keccak hash, too.
+TEST_VALUE = "e167f68d6563d75bb25f3aa49c29ef612d41352dc00606de7cbd630bb2665f51"
+if TEST_VALUE != sha3.sha3_256(b"Hello World").hexdigest():
+ print("pysha3 version is < 1.0. Please install from:")
+ print("https://github.com/tiran/pysha3https://github.com/tiran/pysha3")
+ sys.exit(1)
+
+# In this example, we use three reveal values.
+reveal_num = 3
+version = 1
+
+# We set directly the ascii value because memset(buf, 'A', 20) makes it to 20
+# times "41" in the final string.
+
+# Identity and reveal value of dirauth a
+ID_a = 20 * "41" # RSA identity of 40 base16 bytes.
+R_a = 56 * 'A' # 56 base64 characters
+
+# Identity and reveal value of dirauth b
+ID_b = 20 * "42" # RSA identity of 40 base16 bytes.
+R_b = 56 * 'B' # 56 base64 characters
+
+# Identity and reveal value of dirauth c
+ID_c = 20 * "43" # RSA identity of 40 base16 bytes.
+R_c = 56 * 'C' # 56 base64 characters
+
+# Concatenate them all together and hash them to form HASHED_REVEALS.
+REVEALS = (ID_a + R_a + ID_b + R_b + ID_c + R_c).encode()
+hashed_reveals_object = hashlib.sha3_256(REVEALS)
+hashed_reveals = hashed_reveals_object.digest()
+
+previous_SRV = (32 * 'Z').encode()
+
+# Now form the message.
+#srv_msg = struct.pack('13sQL256ss', "shared-random", reveal_num, version,
+# hashed_reveals, previous_SRV)
+invariant_token = b"shared-random"
+srv_msg = invariant_token + \
+ struct.pack('!QL', reveal_num, version) + \
+ hashed_reveals + \
+ previous_SRV
+
+# Now calculate the HMAC
+srv = hashlib.sha3_256(srv_msg)
+print("%s" % srv.hexdigest().upper())
+
+# 2A9B1D6237DAB312A40F575DA85C147663E7ED3F80E9555395F15B515C74253D
diff --git a/src/test/test.c b/src/test/test.c
index c0faec3..3a1054d 100644
--- a/src/test/test.c
+++ b/src/test/test.c
@@ -1170,6 +1170,7 @@ struct testgroup_t testgroups[] = {
{ "routerset/" , routerset_tests },
{ "scheduler/", scheduler_tests },
{ "socks/", socks_tests },
+ { "shared-random/", sr_tests },
{ "status/" , status_tests },
{ "tortls/", tortls_tests },
{ "util/", util_tests },
diff --git a/src/test/test.h b/src/test/test.h
index 747b61d..6744d25 100644
--- a/src/test/test.h
+++ b/src/test/test.h
@@ -225,6 +225,7 @@ extern struct testcase_t util_format_tests[];
extern struct testcase_t util_process_tests[];
extern struct testcase_t dns_tests[];
extern struct testcase_t handle_tests[];
+extern struct testcase_t sr_tests[];
extern struct testcase_t slow_crypto_tests[];
extern struct testcase_t slow_util_tests[];
diff --git a/src/test/test_dir.c b/src/test/test_dir.c
index b7d58bd..b0ae2d9 100644
--- a/src/test/test_dir.c
+++ b/src/test/test_dir.c
@@ -30,6 +30,7 @@
#include "routerlist.h"
#include "routerparse.h"
#include "routerset.h"
+#include "shared_random_state.h"
#include "test.h"
#include "test_dir_common.h"
#include "torcert.h"
@@ -1544,6 +1545,43 @@ test_dir_param_voting(void *arg)
return;
}
+static void
+test_dir_param_voting_lookup(void *arg)
+{
+ (void)arg;
+ smartlist_t *lst = smartlist_new();
+
+ smartlist_split_string(lst,
+ "moomin=9 moomin=10 moomintroll=5 fred "
+ "jack= electricity=sdk opa=6z abc=9 abcd=99",
+ NULL, 0, 0);
+
+ tt_int_op(1000,
+ OP_EQ, dirvote_get_intermediate_param_value(lst, "ab", 1000));
+ tt_int_op(9, OP_EQ, dirvote_get_intermediate_param_value(lst, "abc", 1000));
+ tt_int_op(99, OP_EQ, dirvote_get_intermediate_param_value(lst, "abcd", 1000));
+
+ /* moomin appears twice. */
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "moomin", -100));
+ /* fred and jack are truncated */
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "fred", -100));
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "jack", -100));
+ /* electricity and opa aren't integers. */
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "electricity", -100));
+ tt_int_op(-100, OP_EQ,
+ dirvote_get_intermediate_param_value(lst, "opa", -100));
+
+ done:
+ SMARTLIST_FOREACH(lst, char *, cp, tor_free(cp));
+ smartlist_free(lst);
+}
+
+#undef dirvote_compute_params
+
/** Helper: Test that two networkstatus_voter_info_t do in fact represent the
* same voting authority, and that they do in fact have all the same
* information. */
@@ -1788,6 +1826,15 @@ test_routerstatus_for_v3ns(routerstatus_t *rs, time_t now)
return;
}
+static authority_cert_t *mock_cert;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
/** Run a unit tests for generating and parsing networkstatuses, with
* the supply test fns. */
static void
@@ -1831,10 +1878,30 @@ test_a_networkstatus(
tt_assert(rs_test);
tt_assert(vrs_test);
- tt_assert(!dir_common_authority_pk_init(&cert1, &cert2, &cert3,
- &sign_skey_1, &sign_skey_2,
- &sign_skey_3));
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+
+ /* Parse certificates and keys. */
+ cert1 = mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(cert1);
+ cert2 = authority_cert_parse_from_string(AUTHORITY_CERT_2, NULL);
+ tt_assert(cert2);
+ cert3 = authority_cert_parse_from_string(AUTHORITY_CERT_3, NULL);
+ tt_assert(cert3);
+ sign_skey_1 = crypto_pk_new();
+ sign_skey_2 = crypto_pk_new();
+ sign_skey_3 = crypto_pk_new();
sign_skey_leg1 = pk_generate(4);
+ sr_state_init(0, 0);
+
+ tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_1,
+ AUTHORITY_SIGNKEY_1, -1));
+ tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_2,
+ AUTHORITY_SIGNKEY_2, -1));
+ tt_assert(!crypto_pk_read_private_key_from_string(sign_skey_3,
+ AUTHORITY_SIGNKEY_3, -1));
+
+ tt_assert(!crypto_pk_cmp_keys(sign_skey_1, cert1->signing_key));
+ tt_assert(!crypto_pk_cmp_keys(sign_skey_2, cert2->signing_key));
tt_assert(!dir_common_construct_vote_1(&vote, cert1, sign_skey_1, vrs_gen,
&v1, &n_vrs, now, 1));
diff --git a/src/test/test_routerlist.c b/src/test/test_routerlist.c
index 34b70ac..088bd25 100644
--- a/src/test/test_routerlist.c
+++ b/src/test/test_routerlist.c
@@ -19,13 +19,24 @@
#include "networkstatus.h"
#include "nodelist.h"
#include "policies.h"
+#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
+#include "shared_random.h"
#include "test.h"
#include "test_dir_common.h"
void construct_consensus(char **consensus_text_md);
+static authority_cert_t *mock_cert;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
/* 4 digests + 3 sep + pre + post + NULL */
static char output[4*BASE64_DIGEST256_LEN+3+2+2+1];
@@ -227,6 +238,12 @@ test_router_pick_directory_server_impl(void *arg)
tt_assert(networkstatus_consensus_is_bootstrapping(now + 2*24*60*60));
tt_assert(networkstatus_consensus_is_bootstrapping(now - 2*24*60*60));
+ /* Init SR subsystem. */
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ sr_init(0);
+ UNMOCK(get_my_v3_authority_cert);
+
/* No consensus available, fail early */
rs = router_pick_directory_server_impl(V3_DIRINFO, (const int) 0, NULL);
tt_assert(rs == NULL);
diff --git a/src/test/test_shared_random.c b/src/test/test_shared_random.c
new file mode 100644
index 0000000..1125717
--- /dev/null
+++ b/src/test/test_shared_random.c
@@ -0,0 +1,1256 @@
+#define SHARED_RANDOM_PRIVATE
+#define SHARED_RANDOM_STATE_PRIVATE
+#define CONFIG_PRIVATE
+#define DIRVOTE_PRIVATE
+
+#include "or.h"
+#include "test.h"
+#include "config.h"
+#include "dirvote.h"
+#include "shared_random.h"
+#include "shared_random_state.h"
+#include "routerkeys.h"
+#include "routerlist.h"
+#include "router.h"
+#include "routerparse.h"
+#include "networkstatus.h"
+
+static authority_cert_t *mock_cert;
+
+static authority_cert_t *
+get_my_v3_authority_cert_m(void)
+{
+ tor_assert(mock_cert);
+ return mock_cert;
+}
+
+/* Setup a minimal dirauth environment by initializing the SR state and
+ * making sure the options are set to be an authority directory. */
+static void
+init_authority_state(void)
+{
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+
+ or_options_t *options = get_options_mutable();
+ mock_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(mock_cert);
+ options->AuthoritativeDir = 1;
+ tt_int_op(0, ==, load_ed_keys(options, time(NULL)));
+ sr_state_init(0, 0);
+ /* It's possible a commit has been generated in our state depending on
+ * the phase we are currently in which uses "now" as the starting
+ * timestamp. Delete it before we do any testing below. */
+ sr_state_delete_commits();
+
+ done:
+ UNMOCK(get_my_v3_authority_cert);
+}
+
+static void
+test_get_sr_protocol_phase(void *arg)
+{
+ time_t the_time;
+ sr_phase_t phase;
+ int retval;
+
+ (void) arg;
+
+ /* Initialize SR state */
+ init_authority_state();
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 23:59:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 00:00:01 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 11:59:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_COMMIT);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 12:00:01 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ }
+
+ {
+ retval = parse_rfc1123_time("Wed, 20 Apr 2015 13:00:00 UTC", &the_time);
+ tt_int_op(retval, ==, 0);
+
+ phase = get_sr_protocol_phase(the_time);
+ tt_int_op(phase, ==, SR_PHASE_REVEAL);
+ }
+
+ done:
+ ;
+}
+
+static networkstatus_t *mock_consensus = NULL;
+
+static void
+test_get_state_valid_until_time(void *arg)
+{
+ time_t current_time;
+ time_t valid_until_time;
+ char tbuf[ISO_TIME_LEN + 1];
+ int retval;
+
+ (void) arg;
+
+ {
+ /* Get the valid until time if called at 00:00:01 */
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC",
+ ¤t_time);
+ tt_int_op(retval, ==, 0);
+ valid_until_time = get_state_valid_until_time(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, valid_until_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 19:22:00 UTC",
+ ¤t_time);
+ tt_int_op(retval, ==, 0);
+ valid_until_time = get_state_valid_until_time(current_time);
+
+ format_iso_time(tbuf, valid_until_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:59:00 UTC",
+ ¤t_time);
+ tt_int_op(retval, ==, 0);
+ valid_until_time = get_state_valid_until_time(current_time);
+
+ format_iso_time(tbuf, valid_until_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+ ¤t_time);
+ tt_int_op(retval, ==, 0);
+ valid_until_time = get_state_valid_until_time(current_time);
+
+ format_iso_time(tbuf, valid_until_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ done:
+ ;
+}
+
+/* Mock function to immediately return our local 'mock_consensus'. */
+static networkstatus_t *
+mock_networkstatus_get_live_consensus(time_t now)
+{
+ (void) now;
+ return mock_consensus;
+}
+
+/** Test the get_next_valid_after_time() function. */
+static void
+test_get_next_valid_after_time(void *arg)
+{
+ time_t current_time;
+ time_t valid_after_time;
+ char tbuf[ISO_TIME_LEN + 1];
+ int retval;
+
+ (void) arg;
+
+ {
+ /* Setup a fake consensus just to get the times out of it, since
+ get_next_valid_after_time() needs them. */
+ mock_consensus = tor_malloc_zero(sizeof(networkstatus_t));
+
+ retval = parse_rfc1123_time("Mon, 13 Jan 2016 16:00:00 UTC",
+ &mock_consensus->fresh_until);
+ tt_int_op(retval, ==, 0);
+
+ retval = parse_rfc1123_time("Mon, 13 Jan 2016 15:00:00 UTC",
+ &mock_consensus->valid_after);
+ tt_int_op(retval, ==, 0);
+
+ MOCK(networkstatus_get_live_consensus,
+ mock_networkstatus_get_live_consensus);
+ }
+
+ {
+ /* Get the valid after time if called at 00:00:00 */
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:00 UTC",
+ ¤t_time);
+ tt_int_op(retval, ==, 0);
+ valid_after_time = get_next_valid_after_time(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, valid_after_time);
+ tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ /* Get the valid until time if called at 00:00:01 */
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 00:00:01 UTC",
+ ¤t_time);
+ tt_int_op(retval, ==, 0);
+ valid_after_time = get_next_valid_after_time(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, valid_after_time);
+ tt_str_op("2015-04-20 01:00:00", OP_EQ, tbuf);
+ }
+
+ {
+ retval = parse_rfc1123_time("Mon, 20 Apr 2015 23:30:01 UTC",
+ ¤t_time);
+ tt_int_op(retval, ==, 0);
+ valid_after_time = get_next_valid_after_time(current_time);
+
+ /* Compare it with the correct result */
+ format_iso_time(tbuf, valid_after_time);
+ tt_str_op("2015-04-21 00:00:00", OP_EQ, tbuf);
+ }
+
+ done:
+ networkstatus_vote_free(mock_consensus);
+}
+
+/* In this test we are going to generate a sr_commit_t object and validate
+ * it. We first generate our values, and then we parse them as if they were
+ * received from the network. After we parse both the commit and the reveal,
+ * we verify that they indeed match. */
+static void
+test_sr_commit(void *arg)
+{
+ authority_cert_t *auth_cert = NULL;
+ time_t now = time(NULL);
+ sr_commit_t *our_commit = NULL;
+ smartlist_t *args = smartlist_new();
+
+ (void) arg;
+
+ { /* Setup a minimal dirauth environment for this test */
+ or_options_t *options = get_options_mutable();
+
+ auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(auth_cert);
+
+ options->AuthoritativeDir = 1;
+ tt_int_op(0, ==, load_ed_keys(options, now));
+ }
+
+ /* Generate our commit object and validate it has the appropriate field
+ * that we can then use to build a representation that we'll find in a
+ * vote coming from the network. */
+ {
+ sr_commit_t test_commit;
+ our_commit = sr_generate_our_commit(now, auth_cert);
+ tt_assert(our_commit);
+ /* Default and only supported algorithm for now. */
+ tt_assert(our_commit->alg == DIGEST_SHA3_256);
+ /* We should have a reveal value. */
+ tt_assert(commit_has_reveal_value(our_commit));
+ /* We should have a random value. */
+ tt_assert(!tor_mem_is_zero((char *) our_commit->random_number,
+ sizeof(our_commit->random_number)));
+ /* Commit and reveal timestamp should be the same. */
+ tt_int_op(our_commit->commit_ts, ==, our_commit->reveal_ts);
+ /* We should have a hashed reveal. */
+ tt_assert(!tor_mem_is_zero(our_commit->hashed_reveal,
+ sizeof(our_commit->hashed_reveal)));
+ /* Do we have a valid encoded commit and reveal. Note the following only
+ * tests if the generated values are correct. Their could be a bug in
+ * the decode function but we test them seperately. */
+ tt_int_op(0, ==, reveal_decode(our_commit->encoded_reveal,
+ &test_commit));
+ tt_int_op(0, ==, commit_decode(our_commit->encoded_commit,
+ &test_commit));
+ tt_int_op(0, ==, verify_commit_and_reveal(our_commit));
+ }
+
+ /* Let's make sure our verify commit and reveal function works. We'll
+ * make it fail a bit with known failure case. */
+ {
+ /* Copy our commit so we don't alter it for the rest of testing. */
+ sr_commit_t test_commit;
+ memcpy(&test_commit, our_commit, sizeof(test_commit));
+
+ /* Timestamp MUST match. */
+ test_commit.commit_ts = test_commit.reveal_ts - 42;
+ tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit));
+ memcpy(&test_commit, our_commit, sizeof(test_commit));
+ tt_int_op(0, ==, verify_commit_and_reveal(&test_commit));
+
+ /* Hashed reveal must match the H(encoded_reveal). */
+ memset(test_commit.hashed_reveal, 'X',
+ sizeof(test_commit.hashed_reveal));
+ tt_int_op(-1, ==, verify_commit_and_reveal(&test_commit));
+ memcpy(&test_commit, our_commit, sizeof(test_commit));
+ tt_int_op(0, ==, verify_commit_and_reveal(&test_commit));
+ }
+
+ /* We'll build a list of values from our commit that our parsing function
+ * takes from a vote line and see if we can parse it correctly. */
+ {
+ sr_commit_t *parsed_commit;
+ smartlist_add(args,
+ tor_strdup(crypto_digest_algorithm_get_name(our_commit->alg)));
+ smartlist_add(args, our_commit->rsa_identity_fpr);
+ smartlist_add(args, our_commit->encoded_commit);
+ smartlist_add(args, our_commit->encoded_reveal);
+ parsed_commit = sr_parse_commit(args);
+ tt_assert(parsed_commit);
+ /* That parsed commit should be _EXACTLY_ like our original commit. */
+ tt_mem_op(parsed_commit, OP_EQ, our_commit, sizeof(*parsed_commit));
+ /* Cleanup */
+ tor_free(smartlist_get(args, 0)); /* strdup here. */
+ smartlist_clear(args);
+ sr_commit_free(parsed_commit);
+ }
+
+ done:
+ smartlist_free(args);
+ sr_commit_free(our_commit);
+}
+
+/* Test the encoding and decoding function for commit and reveal values. */
+static void
+test_encoding(void *arg)
+{
+ (void) arg;
+ int ret, duper_rand = 42;
+ /* Random number is 32 bytes. */
+ char raw_rand[32];
+ time_t ts = 1454333590;
+ char hashed_rand[DIGEST256_LEN], hashed_reveal[DIGEST256_LEN];
+ sr_commit_t parsed_commit;
+
+ /* Encoded commit is: base64-encode( 1454333590 || H(H(42)) ). Remember
+ * that we do no expose the raw bytes of our PRNG to the network thus
+ * explaining the double H(). */
+ static const char *encoded_commit =
+ "AAAAAFavXpZbx2LRneYFSLPCP8DLp9BXfeH5FXzbkxM4iRXKGeA54g==";
+ /* Encoded reveal is: base64-encode( 1454333590 || H(42) ). */
+ static const char *encoded_reveal =
+ "AAAAAFavXpYk9x9kTjiQWUqjHwSAEOdPAfCaurXgjPy173SzYjeC2g==";
+
+ /* Set up our raw random bytes array. */
+ memset(raw_rand, 0, sizeof(raw_rand));
+ memcpy(raw_rand, &duper_rand, sizeof(duper_rand));
+ /* Hash random number. */
+ ret = crypto_digest256(hashed_rand, raw_rand,
+ sizeof(raw_rand), SR_DIGEST_ALG);
+ tt_int_op(0, ==, ret);
+ /* Hash reveal value. */
+ tt_int_op(SR_REVEAL_BASE64_LEN, ==, strlen(encoded_reveal));
+ ret = crypto_digest256(hashed_reveal, encoded_reveal,
+ strlen(encoded_reveal), SR_DIGEST_ALG);
+ tt_int_op(0, ==, ret);
+ tt_int_op(SR_COMMIT_BASE64_LEN, ==, strlen(encoded_commit));
+
+ /* Test our commit/reveal decode functions. */
+ {
+ /* Test the reveal encoded value. */
+ tt_int_op(0, ==, reveal_decode(encoded_reveal, &parsed_commit));
+ tt_uint_op(ts, ==, parsed_commit.reveal_ts);
+ tt_mem_op(hashed_rand, OP_EQ, parsed_commit.random_number,
+ sizeof(hashed_rand));
+
+ /* Test the commit encoded value. */
+ memset(&parsed_commit, 0, sizeof(parsed_commit));
+ tt_int_op(0, ==, commit_decode(encoded_commit, &parsed_commit));
+ tt_uint_op(ts, ==, parsed_commit.commit_ts);
+ tt_mem_op(encoded_commit, OP_EQ, parsed_commit.encoded_commit,
+ sizeof(parsed_commit.encoded_commit));
+ tt_mem_op(hashed_reveal, OP_EQ, parsed_commit.hashed_reveal,
+ sizeof(hashed_reveal));
+ }
+
+ /* Test our commit/reveal encode functions. */
+ {
+ /* Test the reveal encode. */
+ char encoded[SR_REVEAL_BASE64_LEN + 1];
+ parsed_commit.reveal_ts = ts;
+ memcpy(parsed_commit.random_number, hashed_rand,
+ sizeof(parsed_commit.random_number));
+ ret = reveal_encode(&parsed_commit, encoded, sizeof(encoded));
+ tt_int_op(SR_REVEAL_BASE64_LEN, ==, ret);
+ tt_mem_op(encoded_reveal, OP_EQ, encoded, strlen(encoded_reveal));
+ }
+
+ {
+ /* Test the commit encode. */
+ char encoded[SR_COMMIT_BASE64_LEN + 1];
+ parsed_commit.commit_ts = ts;
+ memcpy(parsed_commit.hashed_reveal, hashed_reveal,
+ sizeof(parsed_commit.hashed_reveal));
+ ret = commit_encode(&parsed_commit, encoded, sizeof(encoded));
+ tt_int_op(SR_COMMIT_BASE64_LEN, ==, ret);
+ tt_mem_op(encoded_commit, OP_EQ, encoded, strlen(encoded_commit));
+ }
+
+ done:
+ ;
+}
+
+/** Setup some SRVs in our SR state. If <b>also_current</b> is set, then set
+ * both current and previous SRVs.
+ * Helper of test_vote() and test_sr_compute_srv(). */
+static void
+test_sr_setup_srv(int also_current)
+{
+ sr_srv_t *srv = tor_malloc_zero(sizeof(sr_srv_t));
+ srv->num_reveals = 42;
+ memcpy(srv->value,
+ "ZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZZ",
+ sizeof(srv->value));
+
+ sr_state_set_previous_srv(srv);
+
+ if (also_current) {
+ srv = tor_malloc_zero(sizeof(sr_srv_t));
+ srv->num_reveals = 128;
+ memcpy(srv->value,
+ "NNNNNNNNNNNNNNNNNNNNNNNNNNNNNNNN",
+ sizeof(srv->value));
+
+ sr_state_set_current_srv(srv);
+ }
+}
+
+/* Test anything that has to do with SR protocol and vote. */
+static void
+test_vote(void *arg)
+{
+ int ret;
+ time_t now = time(NULL);
+ sr_commit_t *our_commit = NULL;
+
+ (void) arg;
+
+ { /* Setup a minimal dirauth environment for this test */
+ init_authority_state();
+ /* Set ourself in reveal phase so we can parse the reveal value in the
+ * vote as well. */
+ set_sr_phase(SR_PHASE_REVEAL);
+ }
+
+ /* Generate our commit object and validate it has the appropriate field
+ * that we can then use to build a representation that we'll find in a
+ * vote coming from the network. */
+ {
+ sr_commit_t *saved_commit;
+ our_commit = sr_generate_our_commit(now, mock_cert);
+ tt_assert(our_commit);
+ sr_state_add_commit(our_commit);
+ /* Make sure it's there. */
+ saved_commit = sr_state_get_commit(our_commit->rsa_identity_fpr);
+ tt_assert(saved_commit);
+ }
+
+ /* Also setup the SRVs */
+ test_sr_setup_srv(1);
+
+ { /* Now test the vote generation */
+ smartlist_t *chunks = smartlist_new();
+ smartlist_t *tokens = smartlist_new();
+ /* Get our vote line and validate it. */
+ char *lines = sr_get_string_for_vote();
+ tt_assert(lines);
+ /* Split the lines. We expect 2 here. */
+ ret = smartlist_split_string(chunks, lines, "\n", SPLIT_IGNORE_BLANK, 0);
+ tt_int_op(ret, ==, 4);
+ tt_str_op(smartlist_get(chunks, 0), OP_EQ, "shared-rand-participate");
+ /* Get our commitment line and will validate it agains our commit. The
+ * format is as follow:
+ * "shared-rand-commitment" SP identity SP algname SP COMMIT [SP REVEAL] NL
+ */
+ char *commit_line = smartlist_get(chunks, 1);
+ tt_assert(commit_line);
+ ret = smartlist_split_string(tokens, commit_line, " ", 0, 0);
+ tt_int_op(ret, ==, 5);
+ tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-commit");
+ tt_str_op(smartlist_get(tokens, 1), OP_EQ,
+ crypto_digest_algorithm_get_name(DIGEST_SHA3_256));
+ tt_str_op(smartlist_get(tokens, 2), OP_EQ,
+ our_commit->rsa_identity_fpr);
+ tt_str_op(smartlist_get(tokens, 3), OP_EQ, our_commit->encoded_commit);
+ tt_str_op(smartlist_get(tokens, 4), OP_EQ, our_commit->encoded_reveal);
+
+ /* Finally, does this vote line creates a valid commit object? */
+ smartlist_t *args = smartlist_new();
+ smartlist_add(args, smartlist_get(tokens, 1));
+ smartlist_add(args, smartlist_get(tokens, 2));
+ smartlist_add(args, smartlist_get(tokens, 3));
+ smartlist_add(args, smartlist_get(tokens, 4));
+ sr_commit_t *parsed_commit = sr_parse_commit(args);
+ tt_assert(parsed_commit);
+ tt_mem_op(parsed_commit, ==, our_commit, sizeof(*our_commit));
+
+ /* minor cleanup */
+ SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+ smartlist_clear(tokens);
+
+ /* Now test the previous SRV */
+ char *prev_srv_line = smartlist_get(chunks, 2);
+ tt_assert(prev_srv_line);
+ ret = smartlist_split_string(tokens, prev_srv_line, " ", 0, 0);
+ tt_int_op(ret, ==, 3);
+ tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-previous-value");
+ tt_str_op(smartlist_get(tokens, 1), OP_EQ, "42");
+ tt_str_op(smartlist_get(tokens, 2), OP_EQ,
+ "WlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlpaWlo=");
+
+ /* minor cleanup */
+ SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+ smartlist_clear(tokens);
+
+ /* Now test the current SRV */
+ char *current_srv_line = smartlist_get(chunks, 3);
+ tt_assert(current_srv_line);
+ ret = smartlist_split_string(tokens, current_srv_line, " ", 0, 0);
+ tt_int_op(ret, ==, 3);
+ tt_str_op(smartlist_get(tokens, 0), OP_EQ, "shared-rand-current-value");
+ tt_str_op(smartlist_get(tokens, 1), OP_EQ, "128");
+ tt_str_op(smartlist_get(tokens, 2), OP_EQ,
+ "Tk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk5OTk4=");
+
+ /* Clean up */
+ sr_commit_free(parsed_commit);
+ SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
+ smartlist_free(chunks);
+ SMARTLIST_FOREACH(tokens, char *, s, tor_free(s));
+ smartlist_free(tokens);
+ smartlist_clear(args);
+ smartlist_free(args);
+ }
+
+ done:
+ sr_commit_free(our_commit);
+}
+
+const char *sr_state_str = "Version 1\n"
+ "ValidUntil 2666-04-20 07:16:00\n"
+ "ValidAfter 2666-04-19 07:16:00\n"
+ "Commit sha3-256 FA3CEC2C99DC68D3166B9B6E4FA21A4026C2AB1C "
+ "7M8GdubCAAdh7WUG0DiwRyxTYRKji7HATa7LLJEZ/UAAAAAAVmfUSg== "
+ "AAAAAFZn1EojfIheIw42bjK3VqkpYyjsQFSbv/dxNna3Q8hUEPKpOw==\n"
+ "Commit sha3-256 41E89EDFBFBA44983E21F18F2230A4ECB5BFB543 "
+ "17aUsYuMeRjd2N1r8yNyg7aHqRa6gf4z7QPoxxAZbp0AAAAAVmfUSg==\n"
+ "Commit sha3-256 36637026573A04110CF3E6B1D201FB9A98B88734 "
+ "DDDYtripvdOU+XPEUm5xpU64d9IURSds1xSwQsgeB8oAAAAAVmfUSg==\n"
+ "SharedRandCurrentValue 3 8dWeW12KEzTGEiLGgO1UVJ7Z91CekoRcxt6Q9KhnOFI=\n"
+ "SharedRandPreviousValue 4 qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqo=\n";
+
+/** Create an SR disk state, parse it and validate that the parsing went
+ * well. Yes! */
+static void
+test_state_load_from_disk(void *arg)
+{
+ int ret;
+ char *dir = tor_strdup(get_fname("test_sr_state"));
+ char *sr_state_path = tor_strdup(get_fname("test_sr_state/sr_state"));
+ sr_state_t *the_sr_state = NULL;
+
+ (void) arg;
+
+ /* First try with a nonexistent path. */
+ ret = disk_state_load_from_disk_impl("NONEXISTENTNONEXISTENT");
+ tt_assert(ret == -ENOENT);
+
+ /* Now create a mock state directory and state file */
+#ifdef _WIN32
+ ret = mkdir(dir);
+#else
+ ret = mkdir(dir, 0700);
+#endif
+ tt_assert(ret == 0);
+ ret = write_str_to_file(sr_state_path, sr_state_str, 0);
+ tt_assert(ret == 0);
+
+ /* Try to load the directory itself. Should fail. */
+ ret = disk_state_load_from_disk_impl(dir);
+ tt_assert(ret == -EINVAL);
+
+ /* State should be non-existent at this point. */
+ the_sr_state = get_sr_state();
+ tt_assert(!the_sr_state);
+
+ /* Now try to load the correct file! */
+ ret = disk_state_load_from_disk_impl(sr_state_path);
+ tt_assert(ret == 0);
+
+ /* Check the content of the state */
+ /* XXX check more deeply!!! */
+ the_sr_state = get_sr_state();
+ tt_assert(the_sr_state);
+ tt_assert(the_sr_state->version == 1);
+ tt_assert(digestmap_size(the_sr_state->commits) == 3);
+ tt_assert(the_sr_state->current_srv);
+ tt_assert(the_sr_state->current_srv->num_reveals == 3);
+ tt_assert(the_sr_state->previous_srv);
+
+ /* XXX Now also try loading corrupted state files and make sure parsing
+ fails */
+
+ done:
+ tor_free(dir);
+ tor_free(sr_state_path);
+}
+
+/** Generate three specially crafted commits (based on the test
+ * vector at sr_srv_calc_ref.py). Helper of test_sr_compute_srv(). */
+static void
+test_sr_setup_commits(void)
+{
+ time_t now = time(NULL);
+ sr_commit_t *commit_a, *commit_b, *commit_c, *commit_d;
+ sr_commit_t *place_holder = tor_malloc_zero(sizeof(*place_holder));
+ authority_cert_t *auth_cert = NULL;
+
+ { /* Setup a minimal dirauth environment for this test */
+ or_options_t *options = get_options_mutable();
+
+ auth_cert = authority_cert_parse_from_string(AUTHORITY_CERT_1, NULL);
+ tt_assert(auth_cert);
+
+ options->AuthoritativeDir = 1;
+ tt_int_op(0, ==, load_ed_keys(options, now));
+ }
+
+ /* Generate three dummy commits according to sr_srv_calc_ref.py . Then
+ register them to the SR state. Also register a fourth commit 'd' with no
+ reveal info, to make sure that it will get ignored during SRV
+ calculation. */
+
+ { /* Commit from auth 'a' */
+ commit_a = sr_generate_our_commit(now, auth_cert);
+ tt_assert(commit_a);
+
+ /* Do some surgery on the commit */
+ strlcpy(commit_a->rsa_identity_fpr,
+ "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+ sizeof(commit_a->rsa_identity_fpr));
+ strlcpy(commit_a->encoded_reveal,
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ sizeof(commit_a->encoded_reveal));
+ memcpy(commit_a->hashed_reveal,
+ "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA",
+ sizeof(commit_a->hashed_reveal));
+ }
+
+ { /* Commit from auth 'b' */
+ commit_b = sr_generate_our_commit(now, auth_cert);
+ tt_assert(commit_b);
+
+ /* Do some surgery on the commit */
+ strlcpy(commit_b->rsa_identity_fpr,
+ "bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
+ sizeof(commit_b->rsa_identity_fpr));
+ strlcpy(commit_b->encoded_reveal,
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
+ sizeof(commit_b->encoded_reveal));
+ memcpy(commit_b->hashed_reveal,
+ "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB",
+ sizeof(commit_b->hashed_reveal));
+ }
+
+ { /* Commit from auth 'c' */
+ commit_c = sr_generate_our_commit(now, auth_cert);
+ tt_assert(commit_c);
+
+ /* Do some surgery on the commit */
+ strlcpy(commit_c->rsa_identity_fpr,
+ "ccccccccccccccccccccccccccccccccccccccccccccccccc",
+ sizeof(commit_c->rsa_identity_fpr));
+ strlcpy(commit_c->encoded_reveal,
+ "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
+ sizeof(commit_c->encoded_reveal));
+ memcpy(commit_c->hashed_reveal,
+ "CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC",
+ sizeof(commit_c->hashed_reveal));
+ }
+
+ { /* Commit from auth 'd' */
+ commit_d = sr_generate_our_commit(now, auth_cert);
+ tt_assert(commit_d);
+
+ /* Do some surgery on the commit */
+ strlcpy(commit_d->rsa_identity_fpr,
+ "ddddddddddddddddddddddddddddddddddddddddddddddddd",
+ sizeof(commit_d->rsa_identity_fpr));
+ strlcpy(commit_d->encoded_reveal,
+ "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
+ sizeof(commit_d->encoded_reveal));
+ memcpy(commit_d->hashed_reveal,
+ "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD",
+ sizeof(commit_d->hashed_reveal));
+ /* Clean up its reveal info */
+ memcpy(place_holder, commit_d, sizeof(*place_holder));
+ memset(commit_d->encoded_reveal, 0, sizeof(commit_d->encoded_reveal));
+ tt_assert(!commit_has_reveal_value(commit_d));
+ }
+
+ /* Register commits to state (during commit phase) */
+ set_sr_phase(SR_PHASE_COMMIT);
+ save_commit_to_state(commit_a);
+ save_commit_to_state(commit_b);
+ save_commit_to_state(commit_c);
+ save_commit_to_state(commit_d);
+ tt_int_op(digestmap_size(get_sr_state()->commits), ==, 4);
+
+ /* Now during REVEAL phase save commit D by restoring its reveal. */
+ set_sr_phase(SR_PHASE_REVEAL);
+ save_commit_to_state(place_holder);
+ tt_str_op(commit_d->encoded_reveal, OP_EQ,
+ "DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD");
+ /* Go back to an empty encoded reveal value. */
+ memset(commit_d->encoded_reveal, 0, sizeof(commit_d->encoded_reveal));
+ memset(commit_d->random_number, 0, sizeof(commit_d->random_number));
+ tt_assert(!commit_has_reveal_value(commit_d));
+
+ done:
+ return;
+}
+
+/** Verify that the SRV generation procedure is proper by testing it against
+ * the test vector from ./sr_srv_calc_ref.py. */
+static void
+test_sr_compute_srv(void *arg)
+{
+ (void) arg;
+ sr_srv_t *current_srv = NULL;
+
+#define SRV_TEST_VECTOR \
+ "2A9B1D6237DAB312A40F575DA85C147663E7ED3F80E9555395F15B515C74253D"
+
+ MOCK(trusteddirserver_get_by_v3_auth_digest,
+ trusteddirserver_get_by_v3_auth_digest_m);
+
+ init_authority_state();
+
+ /* Setup the commits for this unittest */
+ test_sr_setup_commits();
+ test_sr_setup_srv(0);
+
+ /* Now switch to reveal phase */
+ set_sr_phase(SR_PHASE_REVEAL);
+
+ /* Compute the SRV */
+ sr_compute_srv();
+
+ /* Check the result against the test vector */
+ current_srv = sr_state_get_current_srv();
+ tt_assert(current_srv);
+ tt_int_op(current_srv->num_reveals, ==, 3);
+ tt_str_op(hex_str((char*)current_srv->value, 32),
+ ==,
+ SRV_TEST_VECTOR);
+
+ done:
+ ;
+}
+
+/** Return a minimal vote document with a current SRV value set to
+ * <b>srv</b>. */
+static networkstatus_t *
+get_test_vote_with_curr_srv(const char *srv)
+{
+ networkstatus_t *vote = tor_malloc_zero(sizeof(networkstatus_t));
+
+ vote->type = NS_TYPE_VOTE;
+ vote->sr_info.participate = 1;
+ vote->sr_info.current_srv = tor_malloc_zero(sizeof(sr_srv_t));
+ vote->sr_info.current_srv->num_reveals = 42;
+ memcpy(vote->sr_info.current_srv->value,
+ srv,
+ sizeof(vote->sr_info.current_srv->value));
+
+ return vote;
+}
+
+/* Mock function to return the value located in the options instead of the
+ * consensus so we can modify it at will. */
+static networkstatus_t *
+mock_networkstatus_get_latest_consensus(void)
+{
+ return mock_consensus;
+}
+
+/* Test the function that picks the right SRV given a bunch of votes. Make sure
+ * that the function returns an SRV iff the majority/agreement requirements are
+ * met. */
+static void
+test_sr_get_majority_srv_from_votes(void *arg)
+{
+ sr_srv_t *chosen_srv;
+ smartlist_t *votes = smartlist_new();
+
+#define SRV_1 "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+#define SRV_2 "BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB"
+
+ (void) arg;
+
+ init_authority_state();
+ /* Make sure our SRV is fresh so we can consider the super majority with
+ * the consensus params of number of agreements needed. */
+ sr_state_set_fresh_srv();
+
+ /* The test relies on the dirauth list being initialized. */
+ clear_dir_servers();
+ add_default_trusted_dir_authorities(V3_DIRINFO);
+ tt_int_op(get_n_authorities(V3_DIRINFO), ==, 9);
+
+ { /* Prepare voting environment with just a single vote. */
+ networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
+ smartlist_add(votes, vote);
+ }
+
+ /* Since it's only one vote with an SRV, it should not achieve majority and
+ hence no SRV will be returned. */
+ chosen_srv = get_majority_srv_from_votes(votes, 1);
+ tt_assert(!chosen_srv);
+
+ { /* Now put in 8 more votes. Let SRV_1 have majority. */
+ int i;
+ /* Now 7 votes believe in SRV_1 */
+ for (i = 0; i < 6; i++) {
+ networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_1);
+ smartlist_add(votes, vote);
+ }
+ /* and 2 votes believe in SRV_2 */
+ for (i = 0; i < 2; i++) {
+ networkstatus_t *vote = get_test_vote_with_curr_srv(SRV_2);
+ smartlist_add(votes, vote);
+ }
+
+ tt_int_op(smartlist_len(votes), ==, 9);
+ }
+
+ /* Now we achieve majority for SRV_1, but not the AuthDirNumSRVAgreements
+ requirement. So still not picking an SRV. */
+ chosen_srv = get_majority_srv_from_votes(votes, 1);
+ tt_assert(!chosen_srv);
+
+ /* We will now lower the AuthDirNumSRVAgreements requirement by tweaking the
+ * consensus parameter and we will try again. This time it should work. */
+ {
+ char *my_net_params;
+ /* Set a dummy consensus parameter in every vote */
+ SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, vote) {
+ vote->net_params = smartlist_new();
+ smartlist_split_string(vote->net_params,
+ "AuthDirNumSRVAgreements=7", NULL, 0, 0);
+ } SMARTLIST_FOREACH_END(vote);
+
+ /* Pretend you are making a consensus out of the votes */
+ mock_consensus = tor_malloc_zero(sizeof(networkstatus_t));
+ mock_consensus->net_params = smartlist_new();
+ my_net_params = dirvote_compute_params(votes, 66, smartlist_len(votes));
+ smartlist_split_string(mock_consensus->net_params, my_net_params, " ",
+ SPLIT_SKIP_SPACE|SPLIT_IGNORE_BLANK, 0);
+ }
+
+ /* Use our fake consensus for finding consensus parameters. */
+ MOCK(networkstatus_get_latest_consensus,
+ mock_networkstatus_get_latest_consensus);
+ chosen_srv = get_majority_srv_from_votes(votes, 1);
+ tt_assert(chosen_srv);
+ tt_int_op(chosen_srv->num_reveals, ==, 42);
+ tt_mem_op(chosen_srv->value, OP_EQ, SRV_1, sizeof(chosen_srv->value));
+
+ done:
+ SMARTLIST_FOREACH(votes, networkstatus_t *, vote,
+ networkstatus_vote_free(vote));
+ smartlist_free(votes);
+ networkstatus_vote_free(mock_consensus);
+ UNMOCK(networkstatus_get_latest_consensus);
+}
+
+static void
+test_utils(void *arg)
+{
+ (void) arg;
+
+ /* Testing srv_dup(). */
+ {
+ sr_srv_t *srv = NULL, *dup_srv = NULL;
+ const char *srv_value =
+ "1BDB7C3E973936E4D13A49F37C859B3DC69C429334CF9412E3FEF6399C52D47A";
+ srv = tor_malloc_zero(sizeof(*srv));
+ srv->num_reveals = 42;
+ memcpy(srv->value, srv_value, sizeof(srv->value));
+ dup_srv = srv_dup(srv);
+ tt_assert(dup_srv);
+ tt_int_op(dup_srv->num_reveals, ==, srv->num_reveals);
+ tt_mem_op(dup_srv->value, OP_EQ, srv->value, sizeof(srv->value));
+ tor_free(srv);
+ tor_free(dup_srv);
+ }
+
+ /* Testing commitments_are_the_same(). Currently, the check is to test the
+ * value of the encoded commit so let's make sure that actually works. */
+ {
+ /* Payload of 55 bytes that is the length of
+ * sr_commit_t->encoded_commit. */
+ const char *payload =
+ "\x5d\xb9\x60\xb6\xcc\x51\x68\x52\x31\xd9\x88\x88\x71\x71\xe0\x30"
+ "\x59\x55\x7f\xcd\x61\xc0\x4b\x05\xb8\xcd\xc1\x48\xe9\xcd\x16\x1f"
+ "\x70\x15\x0c\xfc\xd3\x1a\x75\xd0\x93\x6c\xc4\xe0\x5c\xbe\xe2\x18"
+ "\xc7\xaf\x72\xb6\x7c\x9b\x52";
+ sr_commit_t commit1, commit2;
+ memcpy(commit1.encoded_commit, payload, sizeof(commit1.encoded_commit));
+ memcpy(commit2.encoded_commit, payload, sizeof(commit2.encoded_commit));
+ tt_int_op(commitments_are_the_same(&commit1, &commit2), ==, 1);
+ /* Let's corrupt one of them. */
+ memset(commit1.encoded_commit, 'A', sizeof(commit1.encoded_commit));
+ tt_int_op(commitments_are_the_same(&commit1, &commit2), ==, 0);
+ }
+
+ /* Testing commit_is_authoritative(). */
+ {
+ crypto_pk_t *k = crypto_pk_new();
+ char fp[FINGERPRINT_LEN + 1];
+ sr_commit_t commit;
+
+ tt_assert(!crypto_pk_generate_key(k));
+
+ tt_int_op(0, ==, crypto_pk_get_fingerprint(k, fp, 0));
+ memcpy(fp, commit.rsa_identity_fpr, sizeof(fp));
+ tt_int_op(commit_is_authoritative(&commit, fp), ==, 1);
+ /* Change the pubkey. */
+ memset(commit.rsa_identity_fpr, 0, sizeof(commit.rsa_identity_fpr));
+ tt_int_op(commit_is_authoritative(&commit, fp), ==, 0);
+ }
+
+ /* Testing get_phase_str(). */
+ {
+ tt_str_op(get_phase_str(SR_PHASE_REVEAL), ==, "reveal");
+ tt_str_op(get_phase_str(SR_PHASE_COMMIT), ==, "commit");
+ }
+
+ /* Testing phase transition */
+ {
+ init_authority_state();
+ set_sr_phase(SR_PHASE_COMMIT);
+ tt_int_op(is_phase_transition(SR_PHASE_REVEAL), ==, 1);
+ tt_int_op(is_phase_transition(SR_PHASE_COMMIT), ==, 0);
+ set_sr_phase(SR_PHASE_REVEAL);
+ tt_int_op(is_phase_transition(SR_PHASE_REVEAL), ==, 0);
+ tt_int_op(is_phase_transition(SR_PHASE_COMMIT), ==, 1);
+ /* Junk. */
+ tt_int_op(is_phase_transition(42), ==, 1);
+ }
+
+ done:
+ return;
+}
+
+static void
+test_state_transition(void *arg)
+{
+ sr_state_t *state = NULL;
+ time_t now = time(NULL);
+
+ (void) arg;
+
+ { /* Setup a minimal dirauth environment for this test */
+ init_authority_state();
+ state = get_sr_state();
+ tt_assert(state);
+ }
+
+ /* Test our state reset for a new protocol run. */
+ {
+ /* Add a commit to the state so we can test if the reset cleans the
+ * commits. Also, change all params that we expect to be updated. */
+ sr_commit_t *commit = sr_generate_our_commit(now, mock_cert);
+ tt_assert(commit);
+ sr_state_add_commit(commit);
+ tt_int_op(digestmap_size(state->commits), ==, 1);
+ /* Let's test our delete feature. */
+ sr_state_delete_commits();
+ tt_int_op(digestmap_size(state->commits), ==, 0);
+ /* Add it back so we can continue the rest of the test because after
+ * deletiong our commit will be freed so generate a new one. */
+ commit = sr_generate_our_commit(now, mock_cert);
+ tt_assert(commit);
+ sr_state_add_commit(commit);
+ tt_int_op(digestmap_size(state->commits), ==, 1);
+ state->n_reveal_rounds = 42;
+ state->n_commit_rounds = 43;
+ state->n_protocol_runs = 44;
+ reset_state_for_new_protocol_run(now);
+ tt_int_op(state->n_reveal_rounds, ==, 0);
+ tt_int_op(state->n_commit_rounds, ==, 0);
+ tt_u64_op(state->n_protocol_runs, ==, 45);
+ tt_int_op(digestmap_size(state->commits), ==, 0);
+ }
+
+ /* Test SRV rotation in our state. */
+ {
+ sr_srv_t *cur, *prev;
+ test_sr_setup_srv(1);
+ cur = sr_state_get_current_srv();
+ tt_assert(cur);
+ /* After, current srv should be the previous and then set to NULL. */
+ state_rotate_srv();
+ prev = sr_state_get_previous_srv();
+ tt_assert(prev == cur);
+ tt_assert(!sr_state_get_current_srv());
+ }
+
+ /* New protocol run. */
+ {
+ sr_srv_t *cur;
+ /* Setup some new SRVs so we can confirm that a new protocol run
+ * actually makes them rotate and compute new ones. */
+ test_sr_setup_srv(1);
+ cur = sr_state_get_current_srv();
+ tt_assert(cur);
+ set_sr_phase(SR_PHASE_REVEAL);
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+ new_protocol_run(now);
+ UNMOCK(get_my_v3_authority_cert);
+ /* Rotation happened. */
+ tt_assert(sr_state_get_previous_srv() == cur);
+ /* We are going into COMMIT phase so we had to rotate our SRVs. Usually
+ * our current SRV would be NULL but a new protocol run should make us
+ * compute a new SRV. */
+ tt_assert(sr_state_get_current_srv());
+ /* Also, make sure we did change the current. */
+ tt_assert(sr_state_get_current_srv() != cur);
+ /* We should have our commitment alone. */
+ tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_int_op(state->n_reveal_rounds, ==, 0);
+ tt_int_op(state->n_commit_rounds, ==, 0);
+ /* 46 here since we were at 45 just before. */
+ tt_u64_op(state->n_protocol_runs, ==, 46);
+ }
+
+ /* Cleanup of SRVs. */
+ {
+ sr_state_clean_srvs();
+ tt_assert(!sr_state_get_current_srv());
+ tt_assert(!sr_state_get_previous_srv());
+ }
+
+ done:
+ return;
+}
+
+static void
+test_keep_commit(void *arg)
+{
+ char fp[FINGERPRINT_LEN + 1];
+ sr_commit_t *commit = NULL, *dup_commit = NULL;
+ sr_state_t *state;
+ time_t now = time(NULL);
+
+ (void) arg;
+
+ { /* Setup a minimal dirauth environment for this test */
+ crypto_pk_t *k = crypto_pk_new();
+ /* Have a key that is not the one from our commit. */
+ tt_int_op(0, ==, crypto_pk_generate_key(k));
+ tt_int_op(0, ==, crypto_pk_get_fingerprint(k, fp, 0));
+ init_authority_state();
+ state = get_sr_state();
+ }
+
+ /* Test this very important function that tells us if we should keep a
+ * commit or not in our state. Most of it depends on the phase and what's
+ * in the commit so we'll change the commit as we go. */
+ commit = sr_generate_our_commit(now, mock_cert);
+ tt_assert(commit);
+ /* Set us in COMMIT phase for starter. */
+ set_sr_phase(SR_PHASE_COMMIT);
+ /* We should never keep a commit from a non authoritative authority. */
+ tt_int_op(should_keep_commit(commit, fp, SR_PHASE_COMMIT), ==, 0);
+ /* This should NOT be kept because it has a reveal value in it. */
+ tt_assert(commit_has_reveal_value(commit));
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ SR_PHASE_COMMIT), ==, 0);
+ /* Add it to the state which should return to not keep it. */
+ sr_state_add_commit(commit);
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ SR_PHASE_COMMIT), ==, 0);
+ /* Remove it from state so we can continue our testing. */
+ digestmap_remove(state->commits, commit->rsa_identity_fpr);
+ /* Let's remove our reveal value which should make it OK to keep it. */
+ memset(commit->encoded_reveal, 0, sizeof(commit->encoded_reveal));
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ SR_PHASE_COMMIT), ==, 1);
+
+ /* Let's reset our commit and go into REVEAL phase. */
+ sr_commit_free(commit);
+ commit = sr_generate_our_commit(now, mock_cert);
+ tt_assert(commit);
+ /* Dup the commit so we have one with and one without a reveal value. */
+ dup_commit = tor_malloc_zero(sizeof(*dup_commit));
+ memcpy(dup_commit, commit, sizeof(*dup_commit));
+ memset(dup_commit->encoded_reveal, 0, sizeof(dup_commit->encoded_reveal));
+ set_sr_phase(SR_PHASE_REVEAL);
+ /* We should never keep a commit from a non authoritative authority. */
+ tt_int_op(should_keep_commit(commit, fp, SR_PHASE_REVEAL), ==, 0);
+ /* We shouldn't accept a commit that is not in our state. */
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ SR_PHASE_REVEAL), ==, 0);
+ /* Important to add the commit _without_ the reveal here. */
+ sr_state_add_commit(dup_commit);
+ tt_int_op(digestmap_size(state->commits), ==, 1);
+ /* Our commit should be valid that is authoritative, contains a reveal, be
+ * in the state and commitment and reveal values match. */
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ SR_PHASE_REVEAL), ==, 1);
+ /* The commit shouldn't be kept if it's not verified that is no matchin
+ * hashed reveal. */
+ {
+ /* Let's save the hash reveal so we can restore it. */
+ sr_commit_t place_holder;
+ memcpy(place_holder.hashed_reveal, commit->hashed_reveal,
+ sizeof(place_holder.hashed_reveal));
+ memset(commit->hashed_reveal, 0, sizeof(commit->hashed_reveal));
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ SR_PHASE_REVEAL), ==, 0);
+ memcpy(commit->hashed_reveal, place_holder.hashed_reveal,
+ sizeof(commit->hashed_reveal));
+ }
+ /* We shouldn't keep a commit that has no reveal. */
+ tt_int_op(should_keep_commit(dup_commit, dup_commit->rsa_identity_fpr,
+ SR_PHASE_REVEAL), ==, 0);
+ /* We must not keep a commit that is not the same from the commit phase. */
+ memset(commit->encoded_commit, 0, sizeof(commit->encoded_commit));
+ tt_int_op(should_keep_commit(commit, commit->rsa_identity_fpr,
+ SR_PHASE_REVEAL), ==, 0);
+
+ done:
+ sr_commit_free(commit);
+ sr_commit_free(dup_commit);
+}
+
+static void
+test_state_update(void *arg)
+{
+ time_t commit_phase_time = 1452076000;
+ time_t reveal_phase_time = 1452086800;
+ sr_state_t *state;
+
+ (void) arg;
+
+ {
+ init_authority_state();
+ state = get_sr_state();
+ set_sr_phase(SR_PHASE_COMMIT);
+ /* We'll cheat a bit here and reset the creation time of the state which
+ * will avoid us to compute a valid_after time that fits the commit
+ * phase. */
+ state->valid_after = 0;
+ state->n_reveal_rounds = 0;
+ state->n_commit_rounds = 0;
+ }
+
+ /* We need to mock for the state update function call. */
+ MOCK(get_my_v3_authority_cert, get_my_v3_authority_cert_m);
+
+ /* We are in COMMIT phase here and we'll trigger a state update but no
+ * transition. */
+ sr_state_update(commit_phase_time);
+ tt_int_op(state->valid_after, ==, commit_phase_time);
+ tt_int_op(state->n_commit_rounds, ==, 1);
+ tt_int_op(state->phase, ==, SR_PHASE_COMMIT);
+ tt_int_op(digestmap_size(state->commits), ==, 1);
+
+ /* We are still in the COMMIT phase here but we'll trigger a state
+ * transition to the REVEAL phase. */
+ sr_state_update(reveal_phase_time);
+ tt_int_op(state->phase, ==, SR_PHASE_REVEAL);
+ tt_int_op(state->valid_after, ==, reveal_phase_time);
+ /* Only our commit should be in there. */
+ tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_int_op(state->n_reveal_rounds, ==, 1);
+
+ /* We can't update a state with a valid after _lower_ than the creation
+ * time so here it is. */
+ sr_state_update(commit_phase_time);
+ tt_int_op(state->valid_after, ==, reveal_phase_time);
+
+ /* Finally, let's go back in COMMIT phase so we can test the state update
+ * of a new protocol run. */
+ state->valid_after = 0;
+ sr_state_update(commit_phase_time);
+ tt_int_op(state->valid_after, ==, commit_phase_time);
+ tt_int_op(state->n_commit_rounds, ==, 1);
+ tt_int_op(state->n_reveal_rounds, ==, 0);
+ tt_u64_op(state->n_protocol_runs, ==, 1);
+ tt_int_op(state->phase, ==, SR_PHASE_COMMIT);
+ tt_int_op(digestmap_size(state->commits), ==, 1);
+ tt_assert(state->current_srv);
+
+ done:
+ sr_state_free();
+ UNMOCK(get_my_v3_authority_cert);
+}
+
+struct testcase_t sr_tests[] = {
+ { "get_sr_protocol_phase", test_get_sr_protocol_phase, TT_FORK,
+ NULL, NULL },
+ { "sr_commit", test_sr_commit, TT_FORK,
+ NULL, NULL },
+ { "keep_commit", test_keep_commit, TT_FORK,
+ NULL, NULL },
+ { "encoding", test_encoding, TT_FORK,
+ NULL, NULL },
+ { "get_next_valid_after_time", test_get_next_valid_after_time, TT_FORK,
+ NULL, NULL },
+ { "get_state_valid_until_time", test_get_state_valid_until_time, TT_FORK,
+ NULL, NULL },
+ { "vote", test_vote, TT_FORK,
+ NULL, NULL },
+ { "state_load_from_disk", test_state_load_from_disk, TT_FORK,
+ NULL, NULL },
+ { "sr_compute_srv", test_sr_compute_srv, TT_FORK, NULL, NULL },
+ { "sr_get_majority_srv_from_votes", test_sr_get_majority_srv_from_votes,
+ TT_FORK, NULL, NULL },
+ { "utils", test_utils, TT_FORK, NULL, NULL },
+ { "state_transition", test_state_transition, TT_FORK, NULL, NULL },
+ { "state_update", test_state_update, TT_FORK,
+ NULL, NULL },
+ END_OF_TESTCASES
+};
1
0