sr: Extract shared SR functions

Move most of the shared random functions that are needed outside of the
dirauth module.

At this commit, because dirvote.c hasn't been refactor, it doesn't compile
because some SR functions need a dirvote function.

Furthermore, 5 functions haven't been touched yet because they are dirauth
only but are in used in other C files than the dirauth module ones.

No code behavior change. Only moving code around.

Signed-off-by: David Goulet <dgoulet@torproject.org>
This commit is contained in:
David Goulet 2018-04-24 11:34:31 -04:00
parent 2115a54b4a
commit bdcf3a3839
11 changed files with 313 additions and 257 deletions

View File

@ -76,7 +76,7 @@
#include "router.h"
#include "routerlist.h"
#include "routerparse.h"
#include "dirauth/shared_random.h"
#include "shared_random_common.h"
#ifndef _WIN32
#include <pwd.h>

View File

@ -96,6 +96,7 @@
#include "router.h"
#include "routerlist.h"
#include "shared_random_state.h"
#include "shared_random_common.h"
#include "util.h"
#include "dirauth/dirvote.h"
@ -499,20 +500,6 @@ get_vote_line_from_commit(const sr_commit_t *commit, sr_phase_t phase)
return vote_line;
}
/* Convert a given srv object to a string for the control port. This doesn't
* 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;
}
/* 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. */
@ -875,27 +862,6 @@ get_majority_srv_from_votes(const smartlist_t *votes, int current)
return the_srv;
}
/* Encode the given shared random value and put it in dst. Destination
* buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
void
sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
{
int ret;
/* Extra byte for the NULL terminated char. */
char buf[SR_SRV_VALUE_BASE64_LEN + 1];
tor_assert(dst);
tor_assert(srv);
tor_assert(dst_len >= sizeof(buf));
ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
sizeof(srv->value), 0);
/* Always expect the full length without the NULL byte. */
tor_assert(ret == (sizeof(buf) - 1));
tor_assert(ret <= (int) dst_len);
strlcpy(dst, buf, dst_len);
}
/* Free a commit object. */
void
sr_commit_free_(sr_commit_t *commit)
@ -1037,55 +1003,6 @@ sr_compute_srv(void)
tor_free(reveals);
}
/* Parse a list of arguments from a SRV value either from a vote, consensus
* or from our disk state and return a newly allocated srv object. NULL is
* returned on error.
*
* The arguments' order:
* num_reveals, value
*/
sr_srv_t *
sr_parse_srv(const smartlist_t *args)
{
char *value;
int ok, ret;
uint64_t num_reveals;
sr_srv_t *srv = NULL;
tor_assert(args);
if (smartlist_len(args) < 2) {
goto end;
}
/* First argument is the number of reveal values */
num_reveals = tor_parse_uint64(smartlist_get(args, 0),
10, 0, UINT64_MAX, &ok, NULL);
if (!ok) {
goto end;
}
/* Second and last argument is the shared random value it self. */
value = smartlist_get(args, 1);
if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
goto end;
}
srv = tor_malloc_zero(sizeof(*srv));
srv->num_reveals = num_reveals;
/* We 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;
}
/* Parse a commit from a vote or from our disk state and return a newly
* allocated commit object. NULL is returned on error.
*
@ -1353,84 +1270,6 @@ sr_save_and_cleanup(void)
sr_cleanup();
}
/* Return the current SRV string representation for the control port. Return 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_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;
}
/* Return the previous SRV string representation for the control port. Return
* 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;
}
/* Return current shared random value from the latest consensus. Caller can
* 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;
}
/* Return previous shared random value from the latest consensus. Caller can
* 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;
}
#ifdef TOR_UNIT_TESTS
/* Set the global value of number of SRV agreements so the test can play

View File

@ -109,13 +109,11 @@ void sr_act_post_consensus(const networkstatus_t *consensus);
void sr_handle_received_commits(smartlist_t *commits,
crypto_pk_t *voter_key);
sr_commit_t *sr_parse_commit(const smartlist_t *args);
sr_srv_t *sr_parse_srv(const smartlist_t *args);
char *sr_get_string_for_vote(void);
char *sr_get_string_for_consensus(const smartlist_t *votes,
int32_t num_srv_agreements);
void sr_commit_free_(sr_commit_t *commit);
#define sr_commit_free(sr) FREE_AND_NULL(sr_commit_t, sr_commit_free_, (sr))
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
@ -128,12 +126,6 @@ void sr_compute_srv(void);
sr_commit_t *sr_generate_our_commit(time_t timestamp,
const authority_cert_t *my_rsa_cert);
char *sr_get_current_for_control(void);
char *sr_get_previous_for_control(void);
const sr_srv_t *sr_get_current(const networkstatus_t *ns);
const sr_srv_t *sr_get_previous(const networkstatus_t *ns);
#ifdef SHARED_RANDOM_PRIVATE
/* Encode */

View File

@ -17,6 +17,7 @@
#include "networkstatus.h"
#include "router.h"
#include "shared_random_state.h"
#include "shared_random_common.h"
#include "dirauth/dirvote.h"
@ -54,10 +55,6 @@ DUMMY_TYPECHECK_INSTANCE(sr_disk_state_t);
VAR(#member, conftype, member, initvalue)
/* Our persistent state magic number. */
#define SR_DISK_STATE_MAGIC 0x98AB1254
/* Each protocol phase has 12 rounds */
#define SHARED_RANDOM_N_ROUNDS 12
/* Number of phase we have in a protocol. */
#define SHARED_RANDOM_N_PHASES 2
static int
disk_state_validate_cb(void *old_state, void *state, void *default_state,
@ -116,81 +113,6 @@ get_phase_str(sr_phase_t phase)
return the_string;
}
/* Return the voting interval of the tor vote subsystem. */
static int
get_voting_interval(void)
{
int interval;
networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL));
if (consensus) {
interval = (int)(consensus->fresh_until - consensus->valid_after);
} else {
/* Same for both a testing and real network. We voluntarily ignore the
* InitialVotingInterval since it complexifies things and it doesn't
* affect the SR protocol. */
interval = get_options()->V3AuthVotingInterval;
}
tor_assert(interval > 0);
return interval;
}
/* Given the time <b>now</b>, return the start time of the current round of
* the SR protocol. For example, if it's 23:47:08, the current round thus
* started at 23:47:00 for a voting interval of 10 seconds. */
STATIC time_t
get_start_time_of_current_round(void)
{
const or_options_t *options = get_options();
int voting_interval = get_voting_interval();
/* First, get the start time of the next round */
time_t next_start = dirvote_get_next_valid_after_time();
/* Now roll back next_start by a voting interval to find the start time of
the current round. */
time_t curr_start = dirvote_get_start_of_next_interval(
next_start - voting_interval - 1,
voting_interval,
options->TestingV3AuthVotingStartOffset);
return curr_start;
}
/** Return the start time of the current SR protocol run. For example, if the
* time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this
* function should return 23/06/2017 00:00:00. */
time_t
sr_state_get_start_time_of_current_protocol_run(time_t now)
{
int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
int voting_interval = get_voting_interval();
/* Find the time the current round started. */
time_t beginning_of_current_round = get_start_time_of_current_round();
/* Get current SR protocol round */
int current_round = (now / voting_interval) % total_rounds;
/* Get start time by subtracting the time elapsed from the beginning of the
protocol run */
time_t time_elapsed_since_start_of_run = current_round * voting_interval;
return beginning_of_current_round - time_elapsed_since_start_of_run;
}
/** 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();
}
/* Return the time we should expire the state file created at <b>now</b>.
* We expire the state file in the beginning of the next protocol run. */
STATIC time_t

View File

@ -121,16 +121,11 @@ int sr_state_is_initialized(void);
void sr_state_save(void);
void sr_state_free_all(void);
time_t sr_state_get_start_time_of_current_protocol_run(time_t now);
unsigned int sr_state_get_phase_duration(void);
unsigned int sr_state_get_protocol_run_duration(void);
#ifdef SHARED_RANDOM_STATE_PRIVATE
STATIC int disk_state_load_from_disk_impl(const char *fname);
STATIC sr_phase_t get_sr_protocol_phase(time_t valid_after);
STATIC time_t get_start_time_of_current_round(void);
STATIC time_t get_state_valid_until_time(time_t now);
STATIC const char *get_phase_str(sr_phase_t phase);

View File

@ -28,7 +28,7 @@
#include "rendservice.h"
#include "routerset.h"
#include "router.h"
#include "dirauth/shared_random.h"
#include "shared_random_common.h"
#include "dirauth/shared_random_state.h"
/* Trunnel */

View File

@ -24,7 +24,7 @@
#include "router.h"
#include "routerkeys.h"
#include "routerlist.h"
#include "dirauth/shared_random_state.h"
#include "shared_random_common.h"
#include "statefile.h"
#include "hs_circuit.h"

View File

@ -103,6 +103,7 @@ LIBTOR_A_SOURCES = \
src/or/scheduler.c \
src/or/scheduler_kist.c \
src/or/scheduler_vanilla.c \
src/or/shared_random_common.c \
src/or/statefile.c \
src/or/status.c \
src/or/torcert.c \
@ -260,6 +261,7 @@ ORHEADERS = \
src/or/routerset.h \
src/or/routerparse.h \
src/or/scheduler.h \
src/or/shared_random_common.h \
src/or/statefile.h \
src/or/status.h \
src/or/torcert.h \

View File

@ -74,6 +74,7 @@
#include "entrynodes.h"
#include "torcert.h"
#include "sandbox.h"
#include "shared_random_common.h"
#include "dirauth/shared_random.h"
#undef log

View File

@ -0,0 +1,258 @@
/* Copyright (c) 2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file shared_random_common.c
* \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.
**/
#define SHARED_RANDOM_COMMON_PRIVATE
#include "shared_random_common.h"
#include "config.h"
#include "networkstatus.h"
#include "util.h"
#include "util_format.h"
/* Convert a given srv object to a string for the control port. This doesn't
* 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;
}
/* Return the voting interval of the tor vote subsystem. */
int
get_voting_interval(void)
{
int interval;
networkstatus_t *consensus = networkstatus_get_live_consensus(time(NULL));
if (consensus) {
interval = (int)(consensus->fresh_until - consensus->valid_after);
} else {
/* Same for both a testing and real network. We voluntarily ignore the
* InitialVotingInterval since it complexifies things and it doesn't
* affect the SR protocol. */
interval = get_options()->V3AuthVotingInterval;
}
tor_assert(interval > 0);
return interval;
}
/* Given the time <b>now</b>, return the start time of the current round of
* the SR protocol. For example, if it's 23:47:08, the current round thus
* started at 23:47:00 for a voting interval of 10 seconds. */
time_t
get_start_time_of_current_round(void)
{
const or_options_t *options = get_options();
int voting_interval = get_voting_interval();
/* First, get the start time of the next round */
time_t next_start = dirvote_get_next_valid_after_time();
/* Now roll back next_start by a voting interval to find the start time of
the current round. */
time_t curr_start = dirvote_get_start_of_next_interval(
next_start - voting_interval - 1,
voting_interval,
options->TestingV3AuthVotingStartOffset);
return curr_start;
}
/*
* Public API
*/
/* Encode the given shared random value and put it in dst. Destination
* buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
void
sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
{
int ret;
/* Extra byte for the NULL terminated char. */
char buf[SR_SRV_VALUE_BASE64_LEN + 1];
tor_assert(dst);
tor_assert(srv);
tor_assert(dst_len >= sizeof(buf));
ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
sizeof(srv->value), 0);
/* Always expect the full length without the NULL byte. */
tor_assert(ret == (sizeof(buf) - 1));
tor_assert(ret <= (int) dst_len);
strlcpy(dst, buf, dst_len);
}
/* Return the current SRV string representation for the control port. Return 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_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;
}
/* Return the previous SRV string representation for the control port. Return
* 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;
}
/* Return current shared random value from the latest consensus. Caller can
* 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;
}
/* Return previous shared random value from the latest consensus. Caller can
* 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;
}
/* Parse a list of arguments from a SRV value either from a vote, consensus
* or from our disk state and return a newly allocated srv object. NULL is
* returned on error.
*
* The arguments' order:
* num_reveals, value
*/
sr_srv_t *
sr_parse_srv(const smartlist_t *args)
{
char *value;
int ok, ret;
uint64_t num_reveals;
sr_srv_t *srv = NULL;
tor_assert(args);
if (smartlist_len(args) < 2) {
goto end;
}
/* First argument is the number of reveal values */
num_reveals = tor_parse_uint64(smartlist_get(args, 0),
10, 0, UINT64_MAX, &ok, NULL);
if (!ok) {
goto end;
}
/* Second and last argument is the shared random value it self. */
value = smartlist_get(args, 1);
if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
goto end;
}
srv = tor_malloc_zero(sizeof(*srv));
srv->num_reveals = num_reveals;
/* We 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;
}
/** Return the start time of the current SR protocol run. For example, if the
* time is 23/06/2017 23:47:08 and a full SR protocol run is 24 hours, this
* function should return 23/06/2017 00:00:00. */
time_t
sr_state_get_start_time_of_current_protocol_run(time_t now)
{
int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
int voting_interval = get_voting_interval();
/* Find the time the current round started. */
time_t beginning_of_current_round = get_start_time_of_current_round();
/* Get current SR protocol round */
int current_round = (now / voting_interval) % total_rounds;
/* Get start time by subtracting the time elapsed from the beginning of the
protocol run */
time_t time_elapsed_since_start_of_run = current_round * voting_interval;
return beginning_of_current_round - time_elapsed_since_start_of_run;
}
/** 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();
}

View File

@ -0,0 +1,47 @@
/* Copyright (c) 2018, The Tor Project, Inc. */
/* See LICENSE for licensing information */
/**
* \file shared_random_common.h
* \brief Header file for shared_random_common.c.
**/
#ifndef TOR_SHARED_RANDOM_COMMON_H
#define TOR_SHARED_RANDOM_COMMON_H
/* Dirauth module. */
#include "dirauth/shared_random.h"
/* Helper functions. */
void sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv);
int get_voting_interval(void);
/* Control port functions. */
char *sr_get_current_for_control(void);
char *sr_get_previous_for_control(void);
/* SRV functions. */
const sr_srv_t *sr_get_current(const networkstatus_t *ns);
const sr_srv_t *sr_get_previous(const networkstatus_t *ns);
sr_srv_t *sr_parse_srv(const smartlist_t *args);
/*
* Shared Random State API
*/
/* Each protocol phase has 12 rounds */
#define SHARED_RANDOM_N_ROUNDS 12
/* Number of phase we have in a protocol. */
#define SHARED_RANDOM_N_PHASES 2
time_t sr_state_get_start_time_of_current_protocol_run(time_t now);
unsigned int sr_state_get_phase_duration(void);
unsigned int sr_state_get_protocol_run_duration(void);
time_t get_start_time_of_current_round(void);
#ifdef TOR_UNIT_TESTS
#endif /* TOR_UNIT_TESTS */
#endif /* TOR_SHARED_RANDOM_COMMON_H */