LCOV - code coverage report
Current view: top level - feature/hs_common - shared_random_client.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 74 102 72.5 %
Date: 2021-11-24 03:28:48 Functions: 8 12 66.7 %

          Line data    Source code
       1             : /* Copyright (c) 2018-2021, The Tor Project, Inc. */
       2             : /* See LICENSE for licensing information */
       3             : 
       4             : /**
       5             :  * \file shared_random_client.c
       6             :  * \brief This file contains functions that are from the shared random
       7             :  *        subsystem but used by many part of tor. The full feature is built
       8             :  *        as part of the dirauth module.
       9             :  **/
      10             : 
      11             : #include "feature/hs_common/shared_random_client.h"
      12             : 
      13             : #include "app/config/config.h"
      14             : #include "feature/dirauth/authmode.h"
      15             : #include "feature/dirauth/voting_schedule.h"
      16             : #include "feature/nodelist/microdesc.h"
      17             : #include "feature/nodelist/networkstatus.h"
      18             : #include "lib/encoding/binascii.h"
      19             : 
      20             : #include "feature/nodelist/networkstatus_st.h"
      21             : 
      22             : /** Convert a given srv object to a string for the control port. This doesn't
      23             :  * fail and the srv object MUST be valid. */
      24             : static char *
      25           0 : srv_to_control_string(const sr_srv_t *srv)
      26             : {
      27           0 :   char *srv_str;
      28           0 :   char srv_hash_encoded[SR_SRV_VALUE_BASE64_LEN + 1];
      29           0 :   tor_assert(srv);
      30             : 
      31           0 :   sr_srv_encode(srv_hash_encoded, sizeof(srv_hash_encoded), srv);
      32           0 :   tor_asprintf(&srv_str, "%s", srv_hash_encoded);
      33           0 :   return srv_str;
      34             : }
      35             : 
      36             : /**
      37             :  * If we have no consensus and we are not an authority, assume that this is the
      38             :  * voting interval. This can be used while bootstrapping as a relay and we are
      39             :  * asked to initialize HS stats (see rep_hist_hs_stats_init()) */
      40             : #define DEFAULT_NETWORK_VOTING_INTERVAL (3600)
      41             : #define TESTING_DEFAULT_NETWORK_VOTING_INTERVAL (20)
      42             : 
      43             : /* This is an unpleasing workaround for tests.  Our unit tests assume that we
      44             :  * are scheduling all of our shared random stuff as if we were a directory
      45             :  * authority, but they do not always set V3AuthoritativeDir.
      46             :  */
      47             : #ifdef TOR_UNIT_TESTS
      48             : #define ASSUME_AUTHORITY_SCHEDULING 1
      49             : #else
      50             : #define ASSUME_AUTHORITY_SCHEDULING 0
      51             : #endif
      52             : 
      53             : /** Return the voting interval of the tor vote subsystem. */
      54             : int
      55       69661 : get_voting_interval(void)
      56             : {
      57       69661 :   int interval;
      58       69661 :   networkstatus_t *consensus =
      59       69661 :     networkstatus_get_reasonably_live_consensus(time(NULL),
      60             :                                                 usable_consensus_flavor());
      61             : 
      62       69661 :   if (consensus) {
      63             :     /* Ideally we have a live consensus and we can just use that. */
      64       69443 :     interval = (int)(consensus->fresh_until - consensus->valid_after);
      65         218 :   } else if (authdir_mode(get_options()) || ASSUME_AUTHORITY_SCHEDULING) {
      66             :     /* If we don't have a live consensus and we're an authority,
      67             :      * we should believe our own view of what the schedule ought to be. */
      68         218 :     interval = dirauth_sched_get_configured_interval();
      69             :   } else if ((consensus = networkstatus_get_latest_consensus())) {
      70             :     /* If we're a client, then maybe a latest consensus is good enough?
      71             :      * It's better than falling back to the non-consensus case. */
      72             :     interval = (int)(consensus->fresh_until - consensus->valid_after);
      73             :   } else {
      74             :     /* We can reach this as a relay when bootstrapping and we are asked to
      75             :      * initialize HS stats (see rep_hist_hs_stats_init()). */
      76             :     if (get_options()->TestingTorNetwork) {
      77             :       interval = TESTING_DEFAULT_NETWORK_VOTING_INTERVAL;
      78             :     } else {
      79             :       interval = DEFAULT_NETWORK_VOTING_INTERVAL;
      80             :     }
      81             :   }
      82       69661 :   tor_assert(interval > 0);
      83       69661 :   return interval;
      84             : }
      85             : 
      86             : /*
      87             :  * Public API
      88             :  */
      89             : 
      90             : /** Encode the given shared random value and put it in dst. Destination
      91             :  * buffer must be at least SR_SRV_VALUE_BASE64_LEN plus the NULL byte. */
      92             : void
      93          84 : sr_srv_encode(char *dst, size_t dst_len, const sr_srv_t *srv)
      94             : {
      95          84 :   int ret;
      96             :   /* Extra byte for the NULL terminated char. */
      97          84 :   char buf[SR_SRV_VALUE_BASE64_LEN + 1];
      98             : 
      99          84 :   tor_assert(dst);
     100          84 :   tor_assert(srv);
     101          84 :   tor_assert(dst_len >= sizeof(buf));
     102             : 
     103          84 :   ret = base64_encode(buf, sizeof(buf), (const char *) srv->value,
     104             :                       sizeof(srv->value), 0);
     105             :   /* Always expect the full length without the NULL byte. */
     106          84 :   tor_assert(ret == (sizeof(buf) - 1));
     107          84 :   tor_assert(ret <= (int) dst_len);
     108          84 :   strlcpy(dst, buf, dst_len);
     109          84 : }
     110             : 
     111             : /** Return the current SRV string representation for the control port. Return a
     112             :  * newly allocated string on success containing the value else "" if not found
     113             :  * or if we don't have a valid consensus yet. */
     114             : char *
     115           0 : sr_get_current_for_control(void)
     116             : {
     117           0 :   char *srv_str;
     118           0 :   const networkstatus_t *c = networkstatus_get_latest_consensus();
     119           0 :   if (c && c->sr_info.current_srv) {
     120           0 :     srv_str = srv_to_control_string(c->sr_info.current_srv);
     121             :   } else {
     122           0 :     srv_str = tor_strdup("");
     123             :   }
     124           0 :   return srv_str;
     125             : }
     126             : 
     127             : /** Return the previous SRV string representation for the control port. Return
     128             :  * a newly allocated string on success containing the value else "" if not
     129             :  * found or if we don't have a valid consensus yet. */
     130             : char *
     131           0 : sr_get_previous_for_control(void)
     132             : {
     133           0 :   char *srv_str;
     134           0 :   const networkstatus_t *c = networkstatus_get_latest_consensus();
     135           0 :   if (c && c->sr_info.previous_srv) {
     136           0 :     srv_str = srv_to_control_string(c->sr_info.previous_srv);
     137             :   } else {
     138           0 :     srv_str = tor_strdup("");
     139             :   }
     140           0 :   return srv_str;
     141             : }
     142             : 
     143             : /** Return current shared random value from the latest consensus. Caller can
     144             :  * NOT keep a reference to the returned pointer. Return NULL if none. */
     145             : const sr_srv_t *
     146        9021 : sr_get_current(const networkstatus_t *ns)
     147             : {
     148        9021 :   const networkstatus_t *consensus;
     149             : 
     150             :   /* Use provided ns else get a live one */
     151        9021 :   if (ns) {
     152             :     consensus = ns;
     153             :   } else {
     154           0 :     consensus = networkstatus_get_reasonably_live_consensus(approx_time(),
     155             :                                                   usable_consensus_flavor());
     156             :   }
     157             :   /* Ideally we would never be asked for an SRV without a live consensus. Make
     158             :    * sure this assumption is correct. */
     159        9021 :   tor_assert_nonfatal(consensus);
     160             : 
     161        9021 :   if (consensus) {
     162        9021 :     return consensus->sr_info.current_srv;
     163             :   }
     164             :   return NULL;
     165             : }
     166             : 
     167             : /** Return previous shared random value from the latest consensus. Caller can
     168             :  * NOT keep a reference to the returned pointer. Return NULL if none. */
     169             : const sr_srv_t *
     170        9042 : sr_get_previous(const networkstatus_t *ns)
     171             : {
     172        9042 :   const networkstatus_t *consensus;
     173             : 
     174             :   /* Use provided ns else get a live one */
     175        9042 :   if (ns) {
     176             :     consensus = ns;
     177             :   } else {
     178           0 :     consensus = networkstatus_get_reasonably_live_consensus(approx_time(),
     179             :                                                   usable_consensus_flavor());
     180             :   }
     181             :   /* Ideally we would never be asked for an SRV without a live consensus. Make
     182             :    * sure this assumption is correct. */
     183        9042 :   tor_assert_nonfatal(consensus);
     184             : 
     185        9042 :   if (consensus) {
     186        9042 :     return consensus->sr_info.previous_srv;
     187             :   }
     188             :   return NULL;
     189             : }
     190             : 
     191             : /** Parse a list of arguments from a SRV value either from a vote, consensus
     192             :  * or from our disk state and return a newly allocated srv object. NULL is
     193             :  * returned on error.
     194             :  *
     195             :  * The arguments' order:
     196             :  *    num_reveals, value
     197             :  */
     198             : sr_srv_t *
     199          16 : sr_parse_srv(const smartlist_t *args)
     200             : {
     201          16 :   char *value;
     202          16 :   int ok, ret;
     203          16 :   uint64_t num_reveals;
     204          16 :   sr_srv_t *srv = NULL;
     205             : 
     206          16 :   tor_assert(args);
     207             : 
     208          16 :   if (smartlist_len(args) < 2) {
     209           0 :     goto end;
     210             :   }
     211             : 
     212             :   /* First argument is the number of reveal values */
     213          16 :   num_reveals = tor_parse_uint64(smartlist_get(args, 0),
     214             :                                  10, 0, UINT64_MAX, &ok, NULL);
     215          16 :   if (!ok) {
     216           9 :     goto end;
     217             :   }
     218             :   /* Second and last argument is the shared random value it self. */
     219           7 :   value = smartlist_get(args, 1);
     220           7 :   if (strlen(value) != SR_SRV_VALUE_BASE64_LEN) {
     221           3 :     goto end;
     222             :   }
     223             : 
     224           4 :   srv = tor_malloc_zero(sizeof(*srv));
     225           4 :   srv->num_reveals = num_reveals;
     226             :   /* We subtract one byte from the srclen because the function ignores the
     227             :    * '=' character in the given buffer. This is broken but it's a documented
     228             :    * behavior of the implementation. */
     229           4 :   ret = base64_decode((char *) srv->value, sizeof(srv->value), value,
     230             :                       SR_SRV_VALUE_BASE64_LEN - 1);
     231           4 :   if (ret != sizeof(srv->value)) {
     232           1 :     tor_free(srv);
     233           1 :     srv = NULL;
     234           1 :     goto end;
     235             :   }
     236           3 :  end:
     237          16 :   return srv;
     238             : }
     239             : 
     240             : /** Return the start time of the current SR protocol run using the times from
     241             :  *  the current consensus. For example, if the latest consensus valid-after is
     242             :  *  23/06/2017 23:00:00 and a full SR protocol run is 24 hours, this function
     243             :  *  returns 23/06/2017 00:00:00. */
     244             : time_t
     245       18103 : sr_state_get_start_time_of_current_protocol_run(void)
     246             : {
     247       18103 :   int total_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
     248       18103 :   int voting_interval = get_voting_interval();
     249       18103 :   time_t beginning_of_curr_round;
     250             : 
     251             :   /* This function is not used for voting purposes, so if we have a reasonably
     252             :    * live consensus, use its valid-after as the beginning of the current
     253             :    * round. If we have no consensus but we're an authority, use our own
     254             :    * schedule. Otherwise, try using our view of the voting interval to figure
     255             :    * out when the current round _should_ be starting. */
     256       18103 :   networkstatus_t *ns =
     257       18103 :     networkstatus_get_reasonably_live_consensus(approx_time(),
     258             :                                                 usable_consensus_flavor());
     259       18103 :   if (ns) {
     260       18084 :     beginning_of_curr_round = ns->valid_after;
     261          19 :   } else if (authdir_mode(get_options()) || ASSUME_AUTHORITY_SCHEDULING) {
     262          19 :     beginning_of_curr_round = dirauth_sched_get_cur_valid_after_time();
     263             :   } else {
     264             :     /* voting_interval comes from get_voting_interval(), so if we're in
     265             :      * this case as a client, we already tried to get the voting interval
     266             :      * from the latest_consensus and gave a bug warning if we couldn't.
     267             :      *
     268             :      * We wouldn't want to look at the latest consensus's valid_after time,
     269             :      * since that would be out of date. */
     270             :     beginning_of_curr_round = voting_sched_get_start_of_interval_after(
     271             :                                              approx_time() - voting_interval,
     272             :                                              voting_interval,
     273             :                                              0);
     274             :   }
     275             : 
     276             :   /* Get current SR protocol round */
     277       18103 :   int curr_round_slot;
     278       18103 :   curr_round_slot = (beginning_of_curr_round / voting_interval) % total_rounds;
     279             : 
     280             :   /* Get start time by subtracting the time elapsed from the beginning of the
     281             :      protocol run */
     282       18103 :   time_t time_elapsed_since_start_of_run = curr_round_slot * voting_interval;
     283             : 
     284       18103 :   return beginning_of_curr_round - time_elapsed_since_start_of_run;
     285             : }
     286             : 
     287             : /** Return the start time of the previous SR protocol run. See
     288             :  *  sr_state_get_start_time_of_current_protocol_run() for more details.  */
     289             : time_t
     290           0 : sr_state_get_start_time_of_previous_protocol_run(void)
     291             : {
     292           0 :   time_t start_time_of_current_run =
     293           0 :     sr_state_get_start_time_of_current_protocol_run();
     294             : 
     295             :   /* We get the start time of previous protocol run, by getting the start time
     296             :    * of current run and the subtracting a full protocol run from that. */
     297           0 :   return start_time_of_current_run - sr_state_get_protocol_run_duration();
     298             : }
     299             : 
     300             : /** Return the time (in seconds) it takes to complete a full SR protocol phase
     301             :  *  (e.g. the commit phase). */
     302             : unsigned int
     303       51457 : sr_state_get_phase_duration(void)
     304             : {
     305       51457 :   return SHARED_RANDOM_N_ROUNDS * get_voting_interval();
     306             : }
     307             : 
     308             : /** Return the time (in seconds) it takes to complete a full SR protocol run */
     309             : unsigned int
     310          12 : sr_state_get_protocol_run_duration(void)
     311             : {
     312          12 :   int total_protocol_rounds = SHARED_RANDOM_N_ROUNDS * SHARED_RANDOM_N_PHASES;
     313          12 :   return total_protocol_rounds * get_voting_interval();
     314             : }

Generated by: LCOV version 1.14