2020-01-08 18:39:17 -05:00
|
|
|
/* Copyright (c) 2018-2020, The Tor Project, Inc. */
|
2018-04-24 11:34:31 -04:00
|
|
|
/* See LICENSE for licensing information */
|
|
|
|
|
|
|
|
/**
|
2018-05-01 11:31:33 -04:00
|
|
|
* \file shared_random_client.c
|
2018-04-24 11:34:31 -04:00
|
|
|
* \brief This file contains functions that are from the shared random
|
|
|
|
* subsystem but used by many part of tor. The full feature is built
|
|
|
|
* as part of the dirauth module.
|
|
|
|
**/
|
|
|
|
|
2018-07-05 16:34:59 -04:00
|
|
|
#include "feature/hs_common/shared_random_client.h"
|
2018-04-24 11:34:31 -04:00
|
|
|
|
2018-07-05 16:34:59 -04:00
|
|
|
#include "app/config/config.h"
|
2020-02-24 11:06:44 -05:00
|
|
|
#include "feature/dirauth/authmode.h"
|
2020-02-24 08:53:54 -05:00
|
|
|
#include "feature/dirauth/voting_schedule.h"
|
2018-07-05 16:34:59 -04:00
|
|
|
#include "feature/nodelist/networkstatus.h"
|
2018-06-27 15:59:07 -04:00
|
|
|
#include "lib/encoding/binascii.h"
|
2018-04-24 11:34:31 -04:00
|
|
|
|
2018-07-05 16:34:59 -04:00
|
|
|
#include "feature/nodelist/networkstatus_st.h"
|
2018-06-15 13:45:15 -04:00
|
|
|
|
2019-11-07 08:13:31 -05:00
|
|
|
/** Convert a given srv object to a string for the control port. This doesn't
|
2018-04-24 11:34:31 -04:00
|
|
|
* fail and the srv object MUST be valid. */
|
|
|
|
static char *
|
|
|
|
srv_to_control_string(const sr_srv_t *srv)
|
|
|
|
{
|
|
|
|
char *srv_str;
|
|
|
|
char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
|
|
|
|
tor_assert(srv);
|
|
|
|
|
|
|
|
sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
|
|
|
|
tor_asprintf(&srv_str, "%s", srv_hash_encoded);
|
|
|
|
return srv_str;
|
|
|
|
}
|
|
|
|
|
2020-02-24 11:06:44 -05:00
|
|
|
/**
|
|
|
|
* If we have no consensus and we are not an authority, assume that this is
|
|
|
|
* the voting interval. We should never actually use this: only authorities
|
|
|
|
* should be trying to figure out the schedule when they don't have a
|
|
|
|
* consensus.
|
|
|
|
**/
|
|
|
|
#define DEFAULT_NETWORK_VOTING_INTERVAL (3600)
|
|
|
|
|
|
|
|
/* This is an unpleasing workaround for tests. Our unit tests assume that we
|
|
|
|
* are scheduling all of our shared random stuff as if we were a directory
|
|
|
|
* authority, but they do not always set V3AuthoritativeDir.
|
|
|
|
*/
|
|
|
|
#ifdef TOR_UNIT_TESTS
|
|
|
|
#define ASSUME_AUTHORITY_SCHEDULING 1
|
|
|
|
#else
|
|
|
|
#define ASSUME_AUTHORITY_SCHEDULING 0
|
|
|
|
#endif
|
|
|
|
|
2019-11-07 08:13:31 -05:00
|
|
|
/** Return the voting interval of the tor vote subsystem. */
|
2018-04-24 11:34:31 -04:00
|
|
|
int
|
|
|
|
get_voting_interval(void)
|
|
|
|
{
|
|
|
|
int interval;
|
|
|
|
networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL));
|
|
|
|
|
|
|
|
if (consensus) {
|
|
|
|
interval = (int)(consensus->fresh_until - consensus->valid_after);
|
2020-02-24 11:06:44 -05:00
|
|
|
} else if (authdir_mode(get_options()) || ASSUME_AUTHORITY_SCHEDULING) {
|
|
|
|
interval = dirauth_sched_get_configured_interval();
|
2018-04-24 11:34:31 -04:00
|
|
|
} else {
|
2020-02-24 11:06:44 -05:00
|
|
|
tor_assert_nonfatal_unreached_once();
|
|
|
|
interval = DEFAULT_NETWORK_VOTING_INTERVAL;
|
2018-04-24 11:34:31 -04:00
|
|
|
}
|
|
|
|
tor_assert(interval > 0);
|
|
|
|
return interval;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Public API
|
|
|
|
*/
|
|
|
|
|
2019-11-07 08:13:31 -05:00
|
|
|
/** Encode the given shared random value and put it in dst. Destination
|
2018-04-24 11:34:31 -04:00
|
|
|
* buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
|
|
|
|
void
|
|
|
|
sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
/* Extra byte for the NULL terminated char. */
|
|
|
|
char buf[SR_SRV_VALUE_BASE64_LEN + 1];
|
|
|
|
|
|
|
|
tor_assert(dst);
|
|
|
|
tor_assert(srv);
|
|
|
|
tor_assert(dst_len >= sizeof(buf));
|
|
|
|
|
|
|
|
ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
|
|
|
|
sizeof(srv->value), 0);
|
|
|
|
/* Always expect the full length without the NULL byte. */
|
|
|
|
tor_assert(ret == (sizeof(buf) - 1));
|
|
|
|
tor_assert(ret <= (int) dst_len);
|
|
|
|
strlcpy(dst, buf, dst_len);
|
|
|
|
}
|
|
|
|
|
2019-11-07 08:13:31 -05:00
|
|
|
/** Return the current SRV string representation for the control port. Return a
|
2018-04-24 11:34:31 -04:00
|
|
|
* newly allocated string on success containing the value else "" if not found
|
|
|
|
* or if we don't have a valid consensus yet. */
|
|
|
|
char *
|
|
|
|
sr_get_current_for_control(void)
|
|
|
|
{
|
|
|
|
char *srv_str;
|
|
|
|
const networkstatus_t *c = networkstatus_get_latest_consensus();
|
|
|
|
if (c && c->sr_info.current_srv) {
|
|
|
|
srv_str = srv_to_control_string(c->sr_info.current_srv);
|
|
|
|
} else {
|
|
|
|
srv_str = tor_strdup("");
|
|
|
|
}
|
|
|
|
return srv_str;
|
|
|
|
}
|
|
|
|
|
2019-11-07 08:13:31 -05:00
|
|
|
/** Return the previous SRV string representation for the control port. Return
|
2018-04-24 11:34:31 -04:00
|
|
|
* a newly allocated string on success containing the value else "" if not
|
|
|
|
* found or if we don't have a valid consensus yet. */
|
|
|
|
char *
|
|
|
|
sr_get_previous_for_control(void)
|
|
|
|
{
|
|
|
|
char *srv_str;
|
|
|
|
const networkstatus_t *c = networkstatus_get_latest_consensus();
|
|
|
|
if (c && c->sr_info.previous_srv) {
|
|
|
|
srv_str = srv_to_control_string(c->sr_info.previous_srv);
|
|
|
|
} else {
|
|
|
|
srv_str = tor_strdup("");
|
|
|
|
}
|
|
|
|
return srv_str;
|
|
|
|
}
|
|
|
|
|
2019-11-07 08:13:31 -05:00
|
|
|
/** Return current shared random value from the latest consensus. Caller can
|
2018-04-24 11:34:31 -04:00
|
|
|
* NOT keep a reference to the returned pointer. Return NULL if none. */
|
|
|
|
const sr_srv_t *
|
|
|
|
sr_get_current(const networkstatus_t *ns)
|
|
|
|
{
|
|
|
|
const networkstatus_t *consensus;
|
|
|
|
|
|
|
|
/* Use provided ns else get a live one */
|
|
|
|
if (ns) {
|
|
|
|
consensus = ns;
|
|
|
|
} else {
|
|
|
|
consensus = networkstatus_get_live_consensus(approx_time());
|
|
|
|
}
|
|
|
|
/* Ideally we would never be asked for an SRV without a live consensus. Make
|
|
|
|
* sure this assumption is correct. */
|
|
|
|
tor_assert_nonfatal(consensus);
|
|
|
|
|
|
|
|
if (consensus) {
|
|
|
|
return consensus->sr_info.current_srv;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-11-07 08:13:31 -05:00
|
|
|
/** Return previous shared random value from the latest consensus. Caller can
|
2018-04-24 11:34:31 -04:00
|
|
|
* NOT keep a reference to the returned pointer. Return NULL if none. */
|
|
|
|
const sr_srv_t *
|
|
|
|
sr_get_previous(const networkstatus_t *ns)
|
|
|
|
{
|
|
|
|
const networkstatus_t *consensus;
|
|
|
|
|
|
|
|
/* Use provided ns else get a live one */
|
|
|
|
if (ns) {
|
|
|
|
consensus = ns;
|
|
|
|
} else {
|
|
|
|
consensus = networkstatus_get_live_consensus(approx_time());
|
|
|
|
}
|
|
|
|
/* Ideally we would never be asked for an SRV without a live consensus. Make
|
|
|
|
* sure this assumption is correct. */
|
|
|
|
tor_assert_nonfatal(consensus);
|
|
|
|
|
|
|
|
if (consensus) {
|
|
|
|
return consensus->sr_info.previous_srv;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2019-11-07 08:13:31 -05:00
|
|
|
/** Parse a list of arguments from a SRV value either from a vote, consensus
|
2018-04-24 11:34:31 -04:00
|
|
|
* or from our disk state and return a newly allocated srv object. NULL is
|
|
|
|
* returned on error.
|
|
|
|
*
|
|
|
|
* The arguments' order:
|
|
|
|
* num_reveals, value
|
|
|
|
*/
|
|
|
|
sr_srv_t *
|
|
|
|
sr_parse_srv(const smartlist_t *args)
|
|
|
|
{
|
|
|
|
char *value;
|
|
|
|
int ok, ret;
|
|
|
|
uint64_t num_reveals;
|
|
|
|
sr_srv_t *srv = NULL;
|
|
|
|
|
|
|
|
tor_assert(args);
|
|
|
|
|
|
|
|
if (smartlist_len(args) < 2) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* First argument is the number of reveal values */
|
|
|
|
num_reveals = tor_parse_uint64(smartlist_get(args, 0),
|
|
|
|
10, 0, UINT64_MAX, &ok, NULL);
|
|
|
|
if (!ok) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
/* Second and last argument is the shared random value it self. */
|
|
|
|
value = smartlist_get(args, 1);
|
|
|
|
if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
|
|
|
|
srv = tor_malloc_zero(sizeof(*srv));
|
|
|
|
srv->num_reveals = num_reveals;
|
|
|
|
/* We subtract one byte from the srclen because the function ignores the
|
|
|
|
* '=' character in the given buffer. This is broken but it's a documented
|
|
|
|
* behavior of the implementation. */
|
|
|
|
ret = base64_decode((char *) srv->value, sizeof(srv->value), value,
|
|
|
|
SR_SRV_VALUE_BASE64_LEN - 1);
|
|
|
|
if (ret != sizeof(srv->value)) {
|
|
|
|
tor_free(srv);
|
|
|
|
srv = NULL;
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
end:
|
|
|
|
return srv;
|
|
|
|
}
|
|
|
|
|
2018-07-17 11:00:18 -04:00
|
|
|
/** Return the start time of the current SR protocol run using the times from
|
|
|
|
* the current consensus. For example, if the latest consensus valid-after is
|
|
|
|
* 23/06/2017 23:00:00 and a full SR protocol run is 24 hours, this function
|
|
|
|
* returns 23/06/2017 00:00:00. */
|
2018-04-24 11:34:31 -04:00
|
|
|
time_t
|
2018-07-17 11:00:18 -04:00
|
|
|
sr_state_get_start_time_of_current_protocol_run(void)
|
2018-04-24 11:34:31 -04:00
|
|
|
{
|
|
|
|
int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
|
|
|
|
int voting_interval = get_voting_interval();
|
2018-08-17 15:10:20 +03:00
|
|
|
time_t beginning_of_curr_round;
|
|
|
|
|
|
|
|
/* This function is not used for voting purposes, so if we have a live
|
2020-02-26 08:16:30 -05:00
|
|
|
consensus, use its valid-after as the beginning of the current round.
|
|
|
|
If we have no consensus but we're an authority, use our own
|
|
|
|
schedule. Otherwise, we have a bug somewhere, so we fall back to the
|
|
|
|
default voting interval. */
|
2018-08-17 15:10:20 +03:00
|
|
|
networkstatus_t *ns = networkstatus_get_live_consensus(approx_time());
|
|
|
|
if (ns) {
|
|
|
|
beginning_of_curr_round = ns->valid_after;
|
2020-02-24 11:06:44 -05:00
|
|
|
} else if (authdir_mode(get_options()) || ASSUME_AUTHORITY_SCHEDULING) {
|
|
|
|
beginning_of_curr_round = dirauth_sched_get_cur_valid_after_time();
|
2018-08-17 15:10:20 +03:00
|
|
|
} else {
|
2020-02-24 11:06:44 -05:00
|
|
|
tor_assert_nonfatal_unreached_once();
|
|
|
|
beginning_of_curr_round = voting_sched_get_start_of_interval_after(
|
|
|
|
approx_time() - voting_interval,
|
|
|
|
voting_interval,
|
|
|
|
0);
|
2018-08-17 15:10:20 +03:00
|
|
|
}
|
2018-04-24 11:34:31 -04:00
|
|
|
|
|
|
|
/* Get current SR protocol round */
|
2018-07-17 11:00:18 -04:00
|
|
|
int curr_round_slot;
|
|
|
|
curr_round_slot = (beginning_of_curr_round / voting_interval) % total_rounds;
|
2018-04-24 11:34:31 -04:00
|
|
|
|
|
|
|
/* Get start time by subtracting the time elapsed from the beginning of the
|
|
|
|
protocol run */
|
2018-07-17 11:00:18 -04:00
|
|
|
time_t time_elapsed_since_start_of_run = curr_round_slot * voting_interval;
|
|
|
|
|
|
|
|
log_debug(LD_GENERAL, "Current SRV proto run: Start of current round: %u. "
|
|
|
|
"Time elapsed: %u (%d)", (unsigned) beginning_of_curr_round,
|
|
|
|
(unsigned) time_elapsed_since_start_of_run, voting_interval);
|
|
|
|
|
|
|
|
return beginning_of_curr_round - time_elapsed_since_start_of_run;
|
2018-04-24 11:34:31 -04:00
|
|
|
}
|
|
|
|
|
2018-06-14 15:25:43 +03:00
|
|
|
/** Return the start time of the previous SR protocol run. See
|
|
|
|
* sr_state_get_start_time_of_current_protocol_run() for more details. */
|
|
|
|
time_t
|
2018-07-17 11:00:18 -04:00
|
|
|
sr_state_get_start_time_of_previous_protocol_run(void)
|
2018-06-14 15:25:43 +03:00
|
|
|
{
|
|
|
|
time_t start_time_of_current_run =
|
2018-07-17 11:00:18 -04:00
|
|
|
sr_state_get_start_time_of_current_protocol_run();
|
2018-06-14 15:25:43 +03:00
|
|
|
|
|
|
|
/* We get the start time of previous protocol run, by getting the start time
|
|
|
|
* of current run and the subtracting a full protocol run from that. */
|
|
|
|
return start_time_of_current_run - sr_state_get_protocol_run_duration();
|
2018-04-24 11:34:31 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the time (in seconds) it takes to complete a full SR protocol phase
|
|
|
|
* (e.g. the commit phase). */
|
|
|
|
unsigned int
|
|
|
|
sr_state_get_phase_duration(void)
|
|
|
|
{
|
|
|
|
return SHARED_RANDOM_N_ROUNDS * get_voting_interval();
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Return the time (in seconds) it takes to complete a full SR protocol run */
|
|
|
|
unsigned int
|
|
|
|
sr_state_get_protocol_run_duration(void)
|
|
|
|
{
|
|
|
|
int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
|
|
|
|
return total_protocol_rounds * get_voting_interval();
|
|
|
|
}
|