hs_pow.h (7758B)
1 /* Copyright (c) 2019-2020, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 /** 5 * \file hs_pow.h 6 * \brief Header file containing PoW denial of service defenses for the HS 7 * subsystem for all versions. 8 **/ 9 10 #ifndef TOR_HS_POW_H 11 #define TOR_HS_POW_H 12 13 #include "lib/evloop/compat_libevent.h" 14 #include "lib/evloop/token_bucket.h" 15 #include "lib/smartlist_core/smartlist_core.h" 16 #include "lib/crypt_ops/crypto_ed25519.h" 17 18 /* Service updates the suggested effort every HS_UPDATE_PERIOD seconds. 19 * This parameter controls how often we can change hs descriptor data to 20 * update suggested_effort, but it also controls the frequency of our 21 * opportunities to increase or decrease effort. Lower values react to 22 * attacks faster, higher values may be more stable. 23 * Can this move to torrc? (Or the consensus?) The hs_cache timings are 24 * related, and they're also hardcoded. 25 */ 26 #define HS_UPDATE_PERIOD 300 27 28 /** Length of random nonce (N) used in the PoW scheme. */ 29 #define HS_POW_NONCE_LEN 16 30 /** Length of an E-quiX solution (S) in bytes. */ 31 #define HS_POW_EQX_SOL_LEN 16 32 /** Length of blake2b hash result (R) used in the PoW scheme. */ 33 #define HS_POW_HASH_LEN 4 34 /** Length of algorithm personalization string (P) used in the PoW scheme */ 35 #define HS_POW_PSTRING_LEN 16 36 /** Algorithm personalization string (P) */ 37 #define HS_POW_PSTRING "Tor hs intro v1\0" 38 /** Length of the blinded public ID for the onion service (ID) */ 39 #define HS_POW_ID_LEN 32 40 /** Length of random seed used in the PoW scheme. */ 41 #define HS_POW_SEED_LEN 32 42 /** Length of seed identification heading in the PoW scheme. */ 43 #define HS_POW_SEED_HEAD_LEN 4 44 /** Length of an effort value */ 45 #define HS_POW_EFFORT_LEN sizeof(uint32_t) 46 /** Offset of the nonce value within the challenge string */ 47 #define HS_POW_NONCE_OFFSET \ 48 (HS_POW_PSTRING_LEN + HS_POW_ID_LEN + HS_POW_SEED_LEN) 49 /** Length of a PoW challenge. Construction as per prop327 is: 50 * (P || ID || C || N || INT_32(E)) 51 */ 52 #define HS_POW_CHALLENGE_LEN \ 53 (HS_POW_PSTRING_LEN + HS_POW_ID_LEN + \ 54 HS_POW_SEED_LEN + HS_POW_NONCE_LEN + HS_POW_EFFORT_LEN) 55 56 /** Type of PoW in the descriptor. */ 57 typedef enum { 58 HS_POW_DESC_V1 = 1, 59 } hs_pow_desc_type_t; 60 61 /** Proof-of-Work parameters for DoS defense located in a descriptor. */ 62 typedef struct hs_pow_desc_params_t { 63 /** Type of PoW system being used. */ 64 hs_pow_desc_type_t type; 65 66 /** Random 32-byte seed used as input the the PoW hash function */ 67 uint8_t seed[HS_POW_SEED_LEN]; 68 69 /** Specifies effort value that clients should aim for when contacting the 70 * service. */ 71 uint32_t suggested_effort; 72 73 /** Timestamp after which the above seed expires. */ 74 time_t expiration_time; 75 } hs_pow_desc_params_t; 76 77 /** The inputs to the PoW solver, derived from the descriptor data and the 78 * client's per-connection effort choices. */ 79 typedef struct hs_pow_solver_inputs_t { 80 /** Seed value from a current descriptor */ 81 uint8_t seed[HS_POW_SEED_LEN]; 82 /** Blinded public ID for the onion service this puzzle is bound to */ 83 ed25519_public_key_t service_blinded_id; 84 /** Effort chosen by the client. May be higher or lower than 85 * suggested_effort in the descriptor. */ 86 uint32_t effort; 87 /** Configuration option, choice of hash implementation. AUTOBOOL. */ 88 int CompiledProofOfWorkHash; 89 } hs_pow_solver_inputs_t; 90 91 /** State and parameters of PoW defenses, stored in the service state. */ 92 typedef struct hs_pow_service_state_t { 93 /* If PoW defenses are enabled this is a priority queue containing acceptable 94 * requests that are awaiting rendezvous circuits to built, where priority is 95 * based on the amount of effort that was exerted in the PoW. */ 96 smartlist_t *rend_request_pqueue; 97 98 /* Low level mark for pqueue size. Below this length it's considered to be 99 * effectively empty when calculating effort adjustments. */ 100 int pqueue_low_level; 101 102 /* High level mark for pqueue size. When the queue is this length we will 103 * trim it down to pqueue_high_level/2. */ 104 int pqueue_high_level; 105 106 /* Event callback for dequeueing rend requests, paused when the queue is 107 * empty or rate limited. */ 108 mainloop_event_t *pop_pqueue_ev; 109 110 /* Token bucket for rate limiting the priority queue */ 111 token_bucket_ctr_t pqueue_bucket; 112 113 /* The current seed being used in the PoW defenses. */ 114 uint8_t seed_current[HS_POW_SEED_LEN]; 115 116 /* The previous seed that was used in the PoW defenses. We accept solutions 117 * for both the current and previous seed. */ 118 uint8_t seed_previous[HS_POW_SEED_LEN]; 119 120 /* The time at which the current seed expires and rotates for a new one. */ 121 time_t expiration_time; 122 123 /* The suggested effort that clients should use in order for their request to 124 * be serviced in a timely manner. */ 125 uint32_t suggested_effort; 126 127 /* The maximum effort of a request we've had to trim, this update period */ 128 uint32_t max_trimmed_effort; 129 130 /* The following values are used when calculating and updating the suggested 131 * effort every HS_UPDATE_PERIOD seconds. */ 132 133 /* Number of intro requests the service handled since last update. */ 134 uint32_t rend_handled; 135 /* The next time at which to update the suggested effort. */ 136 time_t next_effort_update; 137 /* Sum of effort of all valid requests received since the last update. */ 138 uint64_t total_effort; 139 140 /* Did we have elements waiting in the queue during this period? */ 141 bool had_queue; 142 /* Are we using pqueue_bucket to rate limit the pqueue? */ 143 bool using_pqueue_bucket; 144 145 } hs_pow_service_state_t; 146 147 /* Struct to store a solution to the PoW challenge. */ 148 typedef struct hs_pow_solution_t { 149 /* The nonce chosen to satisfy the PoW challenge's conditions. */ 150 uint8_t nonce[HS_POW_NONCE_LEN]; 151 152 /* The effort used in this solution. */ 153 uint32_t effort; 154 155 /* A prefix of the seed used in this solution, so it can be identified. */ 156 uint8_t seed_head[HS_POW_SEED_HEAD_LEN]; 157 158 /* The Equi-X solution used in this PoW solution. */ 159 uint8_t equix_solution[HS_POW_EQX_SOL_LEN]; 160 } hs_pow_solution_t; 161 162 #ifdef HAVE_MODULE_POW 163 #define have_module_pow() (1) 164 165 /* API */ 166 int hs_pow_solve(const hs_pow_solver_inputs_t *pow_inputs, 167 hs_pow_solution_t *pow_solution_out); 168 169 int hs_pow_verify(const ed25519_public_key_t *service_blinded_id, 170 const hs_pow_service_state_t *pow_state, 171 const hs_pow_solution_t *pow_solution); 172 173 void hs_pow_remove_seed_from_cache(const uint8_t *seed_head); 174 void hs_pow_free_service_state(hs_pow_service_state_t *state); 175 176 int hs_pow_queue_work(uint32_t intro_circ_identifier, 177 const uint8_t *rend_circ_cookie, 178 const hs_pow_solver_inputs_t *pow_inputs); 179 180 #else /* !defined(HAVE_MODULE_POW) */ 181 #define have_module_pow() (0) 182 183 static inline int 184 hs_pow_solve(const hs_pow_solver_inputs_t *pow_inputs, 185 hs_pow_solution_t *pow_solution_out) 186 { 187 (void)pow_inputs; 188 (void)pow_solution_out; 189 return -1; 190 } 191 192 static inline int 193 hs_pow_verify(const ed25519_public_key_t *service_blinded_id, 194 const hs_pow_service_state_t *pow_state, 195 const hs_pow_solution_t *pow_solution) 196 { 197 (void)service_blinded_id; 198 (void)pow_state; 199 (void)pow_solution; 200 return -1; 201 } 202 203 static inline void 204 hs_pow_remove_seed_from_cache(const uint8_t *seed_head) 205 { 206 (void)seed_head; 207 } 208 209 static inline void 210 hs_pow_free_service_state(hs_pow_service_state_t *state) 211 { 212 (void)state; 213 } 214 215 static inline int 216 hs_pow_queue_work(uint32_t intro_circ_identifier, 217 const uint8_t *rend_circ_cookie, 218 const hs_pow_solver_inputs_t *pow_inputs) 219 { 220 (void)intro_circ_identifier; 221 (void)rend_circ_cookie; 222 (void)pow_inputs; 223 return -1; 224 } 225 226 #endif /* defined(HAVE_MODULE_POW) */ 227 228 #endif /* !defined(TOR_HS_POW_H) */