#define SHARED_RANDOM_PRIVATE
#include "core/or/or.h"
#include "feature/dirauth/shared_random.h"
#include "app/config/config.h"
#include "lib/confmgt/confmgt.h"
#include "lib/crypt_ops/crypto_rand.h"
#include "lib/crypt_ops/crypto_util.h"
#include "feature/nodelist/networkstatus.h"
#include "feature/relay/router.h"
#include "feature/relay/routerkeys.h"
#include "feature/nodelist/dirlist.h"
#include "feature/hs_common/shared_random_client.h"
#include "feature/dirauth/shared_random_state.h"
#include "feature/dirauth/voting_schedule.h"
#include "feature/dirauth/dirvote.h"
#include "feature/dirauth/authmode.h"
#include "feature/dirauth/dirauth_sys.h"
#include "feature/dirauth/dirauth_options_st.h"
#include "feature/nodelist/authority_cert_st.h"
#include "feature/nodelist/networkstatus_st.h"
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";
static int32_t num_srv_agreements_from_vote;
sr_srv_t *
sr_srv_dup(const sr_srv_t *orig)
{
sr_srv_t *duplicate = NULL;
if (!orig) {
return NULL;
}
duplicate = tor_malloc_zero(sizeof(sr_srv_t));
duplicate->num_reveals = orig->num_reveals;
memcpy(duplicate->value, orig->value, sizeof(duplicate->value));
return duplicate;
}
static sr_commit_t *
commit_new(const char *rsa_identity)
{
sr_commit_t *commit;
tor_assert(rsa_identity);
commit = tor_malloc_zero(sizeof(*commit));
commit->alg = SR_DIGEST_ALG;
memcpy(commit->rsa_identity, rsa_identity, sizeof(commit->rsa_identity));
base16_encode(commit->rsa_identity_hex, sizeof(commit->rsa_identity_hex),
commit->rsa_identity, sizeof(commit->rsa_identity));
return commit;
}
static void
commit_log(const sr_commit_t *commit)
{
tor_assert(commit);
log_debug(LD_DIR, "SR: Commit from %s", sr_commit_get_rsa_fpr(commit));
log_debug(LD_DIR, "SR: Commit: [TS: %" PRIu64 "] [Encoded: %s]",
commit->commit_ts, commit->encoded_commit);
log_debug(LD_DIR, "SR: Reveal: [TS: %" PRIu64 "] [Encoded: %s]",
commit->reveal_ts, safe_str(commit->encoded_reveal));
}
STATIC int
verify_commit_and_reveal(const sr_commit_t *commit)
{
tor_assert(commit);
log_debug(LD_DIR, "SR: Validating commit from authority %s",
sr_commit_get_rsa_fpr(commit));
if (commit->commit_ts != commit->reveal_ts) {
log_warn(LD_BUG, "SR: Commit timestamp %" PRIu64 " doesn't match reveal "
"timestamp %" PRIu64, commit->commit_ts,
commit->reveal_ts);
goto invalid;
}
{
char received_hashed_reveal[sizeof(commit->hashed_reveal)];
if (commit->alg != SR_DIGEST_ALG) {
goto invalid;
}
if (crypto_digest256(received_hashed_reveal, commit->encoded_reveal,
SR_REVEAL_BASE64_LEN, commit->alg) < 0) {
goto invalid;
}
if (fast_memneq(received_hashed_reveal, commit->hashed_reveal,
sizeof(received_hashed_reveal))) {
log_warn(LD_BUG, "SR: Received reveal value from authority %s "
"doesn't match the commit value.",
sr_commit_get_rsa_fpr(commit));
goto invalid;
}
}
return 0;
invalid:
return -1;
}
STATIC int
commit_has_reveal_value(const sr_commit_t *commit)
{
return !fast_mem_is_zero(commit->encoded_reveal,
sizeof(commit->encoded_reveal));
}
STATIC int
commit_decode(const char *encoded, sr_commit_t *commit)
{
int decoded_len = 0;
size_t offset = 0;
char b64_decoded[SR_COMMIT_LEN];
tor_assert(encoded);
tor_assert(commit);
if (strlen(encoded) > SR_COMMIT_BASE64_LEN) {
goto error;
}
decoded_len = base64_decode(b64_decoded, sizeof(b64_decoded),
encoded, strlen(encoded));
if (decoded_len < 0) {
log_warn(LD_BUG, "SR: Commit from authority %s can't be decoded.",
sr_commit_get_rsa_fpr(commit));
goto error;
}
if (decoded_len != SR_COMMIT_LEN) {
log_warn(LD_BUG, "SR: Commit from authority %s decoded length doesn't "
"match the expected length (%d vs %u).",
sr_commit_get_rsa_fpr(commit), decoded_len,
(unsigned)SR_COMMIT_LEN);
goto error;
}
commit->commit_ts = tor_ntohll(get_uint64(b64_decoded));
offset += sizeof(uint64_t);
memcpy(commit->hashed_reveal, b64_decoded + offset,
sizeof(commit->hashed_reveal));
strlcpy(commit->encoded_commit, encoded, sizeof(commit->encoded_commit));
return 0;
error:
return -1;
}
STATIC int
reveal_decode(const char *encoded, sr_commit_t *commit)
{
int decoded_len = 0;
char b64_decoded[SR_REVEAL_LEN];
tor_assert(encoded);
tor_assert(commit);
if (strlen(encoded) > SR_REVEAL_BASE64_LEN) {
goto error;
}
decoded_len = base64_decode(b64_decoded, sizeof(b64_decoded),
encoded, strlen(encoded));
if (decoded_len < 0) {
log_warn(LD_BUG, "SR: Reveal from authority %s can't be decoded.",
sr_commit_get_rsa_fpr(commit));
goto error;
}
if (decoded_len != SR_REVEAL_LEN) {
log_warn(LD_BUG, "SR: Reveal from authority %s decoded length is "
"doesn't match the expected length (%d vs %u)",
sr_commit_get_rsa_fpr(commit), decoded_len,
(unsigned)SR_REVEAL_LEN);
goto error;
}
commit->reveal_ts = tor_ntohll(get_uint64(b64_decoded));
memcpy(commit->random_number, b64_decoded + 8,
sizeof(commit->random_number));
strlcpy(commit->encoded_reveal, encoded, sizeof(commit->encoded_reveal));
return 0;
error:
return -1;
}
STATIC int
reveal_encode(const sr_commit_t *commit, char *dst, size_t len)
{
int ret;
size_t offset = 0;
char buf[SR_REVEAL_LEN] = {0};
tor_assert(commit);
tor_assert(dst);
set_uint64(buf, tor_htonll(commit->reveal_ts));
offset += sizeof(uint64_t);
memcpy(buf + offset, commit->random_number,
sizeof(commit->random_number));
memset(dst, 0, len);
ret = base64_encode(dst, len, buf, sizeof(buf), 0);
memwipe(buf, 0, sizeof(buf));
return ret;
}
STATIC int
commit_encode(const sr_commit_t *commit, char *dst, size_t len)
{
size_t offset = 0;
char buf[SR_COMMIT_LEN] = {0};
tor_assert(commit);
tor_assert(dst);
set_uint64(buf, tor_htonll(commit->commit_ts));
offset += sizeof(uint64_t);
memcpy(buf + offset, commit->hashed_reveal,
sizeof(commit->hashed_reveal));
memset(dst, 0, len);
return base64_encode(dst, len, buf, sizeof(buf), 0);
}
static void
sr_cleanup(void)
{
sr_state_free_all();
}
static char *
get_srv_element_from_commit(const sr_commit_t *commit)
{
char *element;
tor_assert(commit);
if (!commit_has_reveal_value(commit)) {
return NULL;
}
tor_asprintf(&element, "%s%s", sr_commit_get_rsa_fpr(commit),
commit->encoded_reveal);
return element;
}
static sr_srv_t *
generate_srv(const char *hashed_reveals, uint64_t reveal_num,
const sr_srv_t *previous_srv)
{
char msg[DIGEST256_LEN + SR_SRV_MSG_LEN] = {0};
size_t offset = 0;
sr_srv_t *srv;
tor_assert(hashed_reveals);
memcpy(msg, SR_SRV_TOKEN, SR_SRV_TOKEN_LEN);
offset += SR_SRV_TOKEN_LEN;
set_uint64(msg + offset, tor_htonll(reveal_num));
offset += sizeof(uint64_t);
set_uint32(msg + offset, htonl(SR_PROTO_VERSION));
offset += sizeof(uint32_t);
memcpy(msg + offset, hashed_reveals, DIGEST256_LEN);
offset += DIGEST256_LEN;
if (previous_srv != NULL) {
memcpy(msg + offset, previous_srv->value, sizeof(previous_srv->value));
}
srv = tor_malloc_zero(sizeof(*srv));
crypto_digest256((char *) srv->value, msg, sizeof(msg), SR_DIGEST_ALG);
srv->num_reveals = reveal_num;
{
char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
log_info(LD_DIR, "SR: Generated SRV: %s", srv_hash_encoded);
}
return srv;
}
static int
compare_reveal_(const void **_a, const void **_b)
{
const sr_commit_t *a = *_a, *b = *_b;
return fast_memcmp(a->hashed_reveal, b->hashed_reveal,
sizeof(a->hashed_reveal));
}
static char *
get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
{
char *vote_line = NULL;
switch (phase) {
case SR_PHASE_COMMIT:
tor_asprintf(&vote_line, "%s %u %s %s %s\n",
commit_ns_str,
SR_PROTO_VERSION,
crypto_digest_algorithm_get_name(commit->alg),
sr_commit_get_rsa_fpr(commit),
commit->encoded_commit);
break;
case SR_PHASE_REVEAL:
{
const char *reveal_str = commit->encoded_reveal;
if (fast_mem_is_zero(commit->encoded_reveal,
sizeof(commit->encoded_reveal))) {
reveal_str = "";
}
tor_asprintf(&vote_line, "%s %u %s %s %s %s\n",
commit_ns_str,
SR_PROTO_VERSION,
crypto_digest_algorithm_get_name(commit->alg),
sr_commit_get_rsa_fpr(commit),
commit->encoded_commit, reveal_str);
break;
}
default:
tor_assert(0);
}
log_debug(LD_DIR, "SR: Commit vote line: %s", vote_line);
return vote_line;
}
static char *
srv_to_ns_string(const sr_srv_t *srv, const char *key)
{
char *srv_str;
char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
tor_assert(srv);
tor_assert(key);
sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
tor_asprintf(&srv_str, "%s %" PRIu64 " %s\n", key,
srv->num_reveals, srv_hash_encoded);
log_debug(LD_DIR, "SR: Consensus SRV line: %s", srv_str);
return srv_str;
}
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);
}
srv_str = smartlist_join_strings(chunks, "", 0, NULL);
SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
smartlist_free(chunks);
return srv_str;
}
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;
}
STATIC int
commit_is_authoritative(const sr_commit_t *commit,
const char *voter_key)
{
tor_assert(commit);
tor_assert(voter_key);
return fast_memeq(commit->rsa_identity, voter_key,
sizeof(commit->rsa_identity));
}
STATIC int
should_keep_commit(const sr_commit_t *commit, const char *voter_key,
sr_phase_t phase)
{
const sr_commit_t *saved_commit;
tor_assert(commit);
tor_assert(voter_key);
log_debug(LD_DIR, "SR: Inspecting commit from %s (voter: %s)?",
sr_commit_get_rsa_fpr(commit),
hex_str(voter_key, DIGEST_LEN));
if (!commit_is_authoritative(commit, voter_key)) {
log_debug(LD_DIR, "SR: Ignoring non-authoritative commit.");
goto ignore;
}
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;
}
saved_commit = sr_state_get_commit(commit->rsa_identity);
switch (phase) {
case SR_PHASE_COMMIT:
if (saved_commit) {
if (!commitments_are_the_same(commit, saved_commit)) {
log_info(LD_DIR,
"SR: Received altered commit from %s in commit phase.",
sr_commit_get_rsa_fpr(commit));
} else {
log_debug(LD_DIR, "SR: Ignoring known commit during commit phase.");
}
goto ignore;
}
if (commit_has_reveal_value(commit)) {
log_warn(LD_DIR, "SR: Commit from authority %s has a reveal value "
"during COMMIT phase. (voter: %s)",
sr_commit_get_rsa_fpr(commit),
hex_str(voter_key, DIGEST_LEN));
goto ignore;
}
break;
case SR_PHASE_REVEAL:
if (!saved_commit) {
log_debug(LD_DIR, "SR: Ignoring commit first seen in reveal phase.");
goto ignore;
}
if (!commitments_are_the_same(commit, saved_commit)) {
log_warn(LD_DIR, "SR: Commit from authority %s is different from "
"previous commit in our state (voter: %s)",
sr_commit_get_rsa_fpr(commit),
hex_str(voter_key, DIGEST_LEN));
goto ignore;
}
if (commit_has_reveal_value(saved_commit)) {
log_debug(LD_DIR, "SR: Ignoring commit with known reveal info.");
goto ignore;
}
if (!commit_has_reveal_value(commit)) {
log_debug(LD_DIR, "SR: Ignoring commit without reveal value.");
goto ignore;
}
if (verify_commit_and_reveal(commit) < 0) {
log_warn(LD_BUG, "SR: Commit from authority %s has an invalid "
"reveal value. (voter: %s)",
sr_commit_get_rsa_fpr(commit),
hex_str(voter_key, DIGEST_LEN));
goto ignore;
}
break;
default:
tor_assert(0);
}
return 1;
ignore:
return 0;
}
STATIC void
save_commit_during_reveal_phase(const sr_commit_t *commit)
{
sr_commit_t *saved_commit;
tor_assert(commit);
saved_commit = sr_state_get_commit(commit->rsa_identity);
tor_assert(saved_commit);
int same_commits = commitments_are_the_same(commit, saved_commit);
tor_assert(same_commits);
sr_state_copy_reveal_info(saved_commit, commit);
}
STATIC void
save_commit_to_state(sr_commit_t *commit)
{
sr_phase_t phase = sr_state_get_phase();
ASSERT_COMMIT_VALID(commit);
switch (phase) {
case SR_PHASE_COMMIT:
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);
}
}
static int
should_keep_srv(int n_agreements)
{
int n_voters = get_n_authorities(V3_DIRINFO);
int votes_required_for_majority = (n_voters / 2) + 1;
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;
}
if (sr_state_srv_is_fresh()) {
if (n_agreements < num_srv_agreements_from_vote) {
log_notice(LD_DIR, "SR: New SRV didn't reach agreement [%d/%d]!",
n_agreements, num_srv_agreements_from_vote);
return 0;
}
}
return 1;
}
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));
}
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);
}
static int
compare_srv_(const void **_a, const void **_b)
{
const sr_srv_t *a = *_a, *b = *_b;
return fast_memcmp(a->value, b->value,
sizeof(a->value));
}
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();
SMARTLIST_FOREACH_BEGIN(votes, networkstatus_t *, v) {
sr_srv_t *srv_tmp = NULL;
if (!v->sr_info.participate) {
continue;
}
srv_tmp = current ? v->sr_info.current_srv : v->sr_info.previous_srv;
if (!srv_tmp) {
continue;
}
smartlist_add(srv_list, srv_tmp);
} SMARTLIST_FOREACH_END(v);
smartlist_sort(srv_list, compare_srv_);
most_frequent_srv = smartlist_get_most_frequent_srv(srv_list, &count);
if (!most_frequent_srv) {
goto end;
}
if (!should_keep_srv(count)) {
goto end;
}
the_srv = most_frequent_srv;
{
char encoded[SR_SRV_VALUE_BASE64_LEN + 1];
sr_srv_encode(encoded, sizeof(encoded), the_srv);
log_debug(LD_DIR, "SR: Chosen SRV by majority: %s (%d votes)", encoded,
count);
}
end:
smartlist_free(srv_list);
return the_srv;
}
void
sr_commit_free_(sr_commit_t *commit)
{
if (commit == NULL) {
return;
}
memwipe(commit->random_number, 0, sizeof(commit->random_number));
tor_free(commit);
}
sr_commit_t *
sr_generate_our_commit(time_t timestamp, const authority_cert_t *my_rsa_cert)
{
sr_commit_t *commit = NULL;
char digest[DIGEST_LEN];
tor_assert(my_rsa_cert);
if (crypto_pk_get_digest(my_rsa_cert->identity_key, digest) < 0) {
goto error;
}
commit = commit_new(digest);
crypto_strongest_rand(commit->random_number,
sizeof(commit->random_number));
commit->commit_ts = commit->reveal_ts = timestamp;
if (reveal_encode(commit, commit->encoded_reveal,
sizeof(commit->encoded_reveal)) < 0) {
log_err(LD_DIR, "SR: Unable to encode our reveal value!");
goto error;
}
tor_assert(commit->alg == SR_DIGEST_ALG);
if (crypto_digest256(commit->hashed_reveal, commit->encoded_reveal,
SR_REVEAL_BASE64_LEN, commit->alg) < 0) {
goto error;
}
if (commit_encode(commit, commit->encoded_commit,
sizeof(commit->encoded_commit)) < 0) {
log_err(LD_DIR, "SR: Unable to encode our commit value!");
goto error;
}
log_debug(LD_DIR, "SR: Generated our commitment:");
commit_log(commit);
commit->valid = 1;
return commit;
error:
sr_commit_free(commit);
return NULL;
}
void
sr_compute_srv(void)
{
uint64_t reveal_num = 0;
char *reveals = NULL;
smartlist_t *chunks, *commits;
digestmap_t *state_commits;
if (BUG(sr_state_get_phase() != SR_PHASE_REVEAL))
return;
state_commits = sr_state_get_commits();
commits = smartlist_new();
chunks = smartlist_new();
DIGESTMAP_FOREACH(state_commits, key, sr_commit_t *, c) {
ASSERT_COMMIT_VALID(c);
if (trusteddirserver_get_by_v3_auth_digest(c->rsa_identity) == NULL) {
log_warn(LD_DIR, "SR: Fingerprint %s is not from a recognized "
"authority. Discarding commit for the SRV computation.",
sr_commit_get_rsa_fpr(c));
continue;
}
smartlist_add(commits, c);
} DIGESTMAP_FOREACH_END;
smartlist_sort(commits, compare_reveal_);
SMARTLIST_FOREACH_BEGIN(commits, const sr_commit_t *, c) {
char *element = get_srv_element_from_commit(c);
if (element) {
smartlist_add(chunks, element);
reveal_num++;
}
} SMARTLIST_FOREACH_END(c);
smartlist_free(commits);
{
sr_srv_t *current_srv;
char hashed_reveals[DIGEST256_LEN];
reveals = smartlist_join_strings(chunks, "", 0, NULL);
SMARTLIST_FOREACH(chunks, char *, s, tor_free(s));
smartlist_free(chunks);
if (crypto_digest256(hashed_reveals, reveals, strlen(reveals),
SR_DIGEST_ALG) < 0) {
goto end;
}
current_srv = generate_srv(hashed_reveals, reveal_num,
sr_state_get_previous_srv());
sr_state_set_current_srv(current_srv);
sr_state_set_fresh_srv();
}
end:
tor_free(reveals);
}
sr_commit_t *
sr_parse_commit(const smartlist_t *args)
{
uint32_t version;
char *value, digest[DIGEST_LEN];
digest_algorithm_t alg;
const char *rsa_identity_fpr;
sr_commit_t *commit = NULL;
if (smartlist_len(args) < 4) {
goto error;
}
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;
}
value = smartlist_get(args, 1);
alg = crypto_digest_algorithm_parse_name(value);
if (alg != SR_DIGEST_ALG) {
log_warn(LD_BUG, "SR: Commit algorithm %s is not recognized.",
escaped(value));
goto error;
}
rsa_identity_fpr = smartlist_get(args, 2);
if (base16_decode(digest, DIGEST_LEN, rsa_identity_fpr,
HEX_DIGEST_LEN) < 0) {
log_warn(LD_DIR, "SR: RSA fingerprint %s not decodable",
escaped(rsa_identity_fpr));
goto error;
}
commit = commit_new(digest);
value = smartlist_get(args, 3);
if (commit_decode(value, commit) < 0) {
goto error;
}
if (smartlist_len(args) > 4) {
value = smartlist_get(args, 4);
if (reveal_decode(value, commit) < 0) {
goto error;
}
}
return commit;
error:
sr_commit_free(commit);
return NULL;
}
void
sr_handle_received_commits(smartlist_t *commits, crypto_pk_t *voter_key)
{
char rsa_identity[DIGEST_LEN];
tor_assert(voter_key);
if (commits == NULL) {
return;
}
if (crypto_pk_get_digest(voter_key, rsa_identity) < 0) {
return;
}
SMARTLIST_FOREACH_BEGIN(commits, sr_commit_t *, commit) {
SMARTLIST_DEL_CURRENT(commits, commit);
if (!should_keep_commit(commit, rsa_identity,
sr_state_get_phase())) {
sr_commit_free(commit);
continue;
}
commit->valid = 1;
save_commit_to_state(commit);
} SMARTLIST_FOREACH_END(commit);
}
char *
sr_get_string_for_vote(void)
{
char *vote_str = NULL;
digestmap_t *state_commits;
smartlist_t *chunks = smartlist_new();
const dirauth_options_t *options = dirauth_get_options();
if (!options->AuthDirSharedRandomness) {
goto end;
}
log_debug(LD_DIR, "SR: Preparing our vote info:");
{
char *sr_flag_line;
tor_asprintf(&sr_flag_line, "%s\n", sr_flag_ns_str);
smartlist_add(chunks, sr_flag_line);
}
state_commits = sr_state_get_commits();
smartlist_t *state_commit_vote_lines = smartlist_new();
DIGESTMAP_FOREACH(state_commits, key, const sr_commit_t *, commit) {
char *line = get_vote_line_from_commit(commit, sr_state_get_phase());
smartlist_add(state_commit_vote_lines, line);
} DIGESTMAP_FOREACH_END;
smartlist_sort_strings(state_commit_vote_lines);
smartlist_add_all(chunks, state_commit_vote_lines);
smartlist_free(state_commit_vote_lines);
{
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;
}
char *
sr_get_string_for_consensus(const smartlist_t *votes,
int32_t num_srv_agreements)
{
char *srv_str;
const dirauth_options_t *options = dirauth_get_options();
tor_assert(votes);
if (!options->AuthDirSharedRandomness) {
log_info(LD_DIR, "SR: Support disabled (AuthDirSharedRandomness %d)",
options->AuthDirSharedRandomness);
goto end;
}
num_srv_agreements_from_vote = num_srv_agreements;
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;
}
void
sr_act_post_consensus(const networkstatus_t *consensus)
{
const or_options_t *options = get_options();
if (!sr_state_is_initialized() || !authdir_mode_v3(options) ||
authdir_mode_bridge(options)) {
return;
}
if (consensus) {
sr_state_clean_srvs();
sr_state_unset_fresh_srv();
sr_state_set_previous_srv(sr_srv_dup(consensus->sr_info.previous_srv));
sr_state_set_current_srv(sr_srv_dup(consensus->sr_info.current_srv));
}
sr_state_update(dirauth_sched_get_next_valid_after_time());
}
int
sr_init(int save_to_disk)
{
return sr_state_init(save_to_disk, 1);
}
void
sr_save_and_cleanup(void)
{
sr_state_save();
sr_cleanup();
}
#ifdef TOR_UNIT_TESTS
void
set_num_srv_agreements(int32_t value)
{
num_srv_agreements_from_vote = value;
}
#endif