summaryrefslogtreecommitdiff
path: root/src/feature/hs/hs_client.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/hs/hs_client.c')
-rw-r--r--src/feature/hs/hs_client.c130
1 files changed, 84 insertions, 46 deletions
diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c
index 2a620da953..6303b8fe75 100644
--- a/src/feature/hs/hs_client.c
+++ b/src/feature/hs/hs_client.c
@@ -549,6 +549,7 @@ find_desc_intro_point_by_ident(const hs_ident_circuit_t *ident,
tor_assert(ident);
tor_assert(desc);
+ tor_assert_nonfatal(!ed25519_public_key_is_zero(&ident->intro_auth_pk));
SMARTLIST_FOREACH_BEGIN(desc->encrypted_data.intro_points,
const hs_desc_intro_point_t *, ip) {
@@ -634,15 +635,8 @@ send_introduce1(origin_circuit_t *intro_circ,
return -1; /* transient failure */
}
- /* Cell has been sent successfully. Copy the introduction point
- * authentication and encryption key in the rendezvous circuit identifier so
- * we can compute the ntor keys when we receive the RENDEZVOUS2 cell. */
- memcpy(&rend_circ->hs_ident->intro_enc_pk, &ip->enc_key,
- sizeof(rend_circ->hs_ident->intro_enc_pk));
- ed25519_pubkey_copy(&rend_circ->hs_ident->intro_auth_pk,
- &intro_circ->hs_ident->intro_auth_pk);
-
- /* Now, we wait for an ACK or NAK on this circuit. */
+ /* Cell has been sent successfully.
+ * Now, we wait for an ACK or NAK on this circuit. */
circuit_change_purpose(TO_CIRCUIT(intro_circ),
CIRCUIT_PURPOSE_C_INTRODUCE_ACK_WAIT);
/* Set timestamp_dirty, because circuit_expire_building expects it to
@@ -657,6 +651,17 @@ send_introduce1(origin_circuit_t *intro_circ,
* tackle. If asked for higher, we solve it at this cap. */
#define CLIENT_MAX_POW_EFFORT 10000
+/** Set a client-side minimum effort. If the client is choosing to increase
+ * effort on retry, it will always pick a value >= this lower limit. */
+#define CLIENT_MIN_RETRY_POW_EFFORT 8
+
+/** Client effort will double on every retry until this level is hit */
+#define CLIENT_POW_EFFORT_DOUBLE_UNTIL 1000
+
+/** After we reach DOUBLE_UNTIL, client effort is multiplied by this amount
+ * on every retry until we reach MAX_POW_EFFORT. */
+#define CLIENT_POW_RETRY_MULTIPLIER (1.5f)
+
/** Send an INTRODUCE1 cell along the intro circuit and populate the rend
* circuit identifier with the needed key material for the e2e encryption.
* Return 0 on success, -1 if there is a transient error such that an action
@@ -731,37 +736,76 @@ consider_sending_introduce1(origin_circuit_t *intro_circ,
goto perm_err;
}
- /* If the descriptor contains PoW parameters then the service is
- * expecting a PoW solution in the INTRODUCE cell, which we solve here. */
- if (have_module_pow() &&
- desc->encrypted_data.pow_params &&
- desc->encrypted_data.pow_params->suggested_effort > 0) {
- log_debug(LD_REND, "PoW params present in descriptor.");
+ /* Copy the introduction point authentication and encryption key
+ * in the rendezvous circuit identifier so we can compute the ntor keys
+ * when we receive the RENDEZVOUS2 cell. */
+ memcpy(&rend_circ->hs_ident->intro_enc_pk, &ip->enc_key,
+ sizeof(rend_circ->hs_ident->intro_enc_pk));
- /* make sure we can't be tricked into hopeless quests */
- if (desc->encrypted_data.pow_params->suggested_effort >
- CLIENT_MAX_POW_EFFORT) {
+ /* Optionally choose to solve a client puzzle for this connection. This
+ * is only available if we have PoW support at compile time, and if the
+ * service has provided a PoW seed in its descriptor. The puzzle is enabled
+ * any time effort is nonzero, which can be recommended by the service or
+ * self-imposed as a result of previous timeouts.
+ */
+ if (have_module_pow() && desc->encrypted_data.pow_params) {
+ hs_pow_solver_inputs_t pow_inputs = {
+ .effort = desc->encrypted_data.pow_params->suggested_effort
+ };
+ memcpy(pow_inputs.seed, desc->encrypted_data.pow_params->seed,
+ sizeof pow_inputs.seed);
+ log_debug(LD_REND, "PoW params present in descriptor, suggested_effort=%u",
+ pow_inputs.effort);
+
+ if (pow_inputs.effort > CLIENT_MAX_POW_EFFORT) {
log_notice(LD_REND, "Onion service suggested effort %d which is "
"higher than we want to solve. Solving at %d instead.",
- desc->encrypted_data.pow_params->suggested_effort,
- CLIENT_MAX_POW_EFFORT);
-
- /* clobber it in-place. hopefully this won't have bad side effects. */
- desc->encrypted_data.pow_params->suggested_effort =
- CLIENT_MAX_POW_EFFORT;
+ pow_inputs.effort, CLIENT_MAX_POW_EFFORT);
+ pow_inputs.effort = CLIENT_MAX_POW_EFFORT;
}
- /* send it to the client-side pow cpuworker for solving. */
- intro_circ->hs_currently_solving_pow = 1;
- if (0 != hs_pow_queue_work(intro_circ->global_identifier,
- rend_circ->global_identifier,
- desc->encrypted_data.pow_params)) {
- log_debug(LD_REND, "Failed to enqueue PoW request");
+ const hs_cache_intro_state_t *state =
+ hs_cache_client_intro_state_find(&intro_circ->hs_ident->identity_pk,
+ &intro_circ->hs_ident->intro_auth_pk);
+ uint32_t unreachable_count = state ? state->unreachable_count : 0;
+ if (state) {
+ log_debug(LD_REND, "hs_cache state during PoW consideration, "
+ "error=%d timed_out=%d unreachable_count=%u",
+ state->error, state->timed_out, state->unreachable_count);
+ }
+ uint64_t new_effort = pow_inputs.effort;
+ for (unsigned n_retry = 0; n_retry < unreachable_count; n_retry++) {
+ if (new_effort >= CLIENT_MAX_POW_EFFORT) {
+ break;
+ }
+ if (new_effort < CLIENT_POW_EFFORT_DOUBLE_UNTIL) {
+ new_effort <<= 1;
+ } else {
+ new_effort = (uint64_t) (CLIENT_POW_RETRY_MULTIPLIER * new_effort);
+ }
+ new_effort = MAX((uint64_t)CLIENT_MIN_RETRY_POW_EFFORT, new_effort);
+ new_effort = MIN((uint64_t)CLIENT_MAX_POW_EFFORT, new_effort);
+ }
+ if (pow_inputs.effort != (uint32_t)new_effort) {
+ log_notice(LD_REND, "Increasing PoW effort from %d to %d after intro "
+ "point unreachable_count=%d",
+ pow_inputs.effort, (int)new_effort, unreachable_count);
+ pow_inputs.effort = (uint32_t)new_effort;
}
- /* can't proceed with the intro1 cell yet, so yield back to the
- * main loop */
- goto tran_err;
+ if (pow_inputs.effort > 0) {
+ /* send it to the client-side pow cpuworker for solving. */
+ intro_circ->hs_currently_solving_pow = 1;
+ if (hs_pow_queue_work(intro_circ->global_identifier,
+ rend_circ->global_identifier,
+ &pow_inputs) != 0) {
+ log_warn(LD_REND, "Failed to enqueue PoW request");
+ }
+
+ /* can't proceed with the intro1 cell yet, so yield back to the
+ * main loop */
+ goto tran_err;
+ }
}
/* move on to the next phase: actually try to send it */
@@ -796,8 +840,8 @@ consider_sending_introduce1(origin_circuit_t *intro_circ,
*
* Return 0 if everything went well, otherwise return -1 in the case of errors.
*/
-static int
-setup_intro_circ_auth_key(origin_circuit_t *circ)
+int
+hs_client_setup_intro_circ_auth_key(origin_circuit_t *circ)
{
const hs_descriptor_t *desc;
const hs_desc_intro_point_t *ip;
@@ -843,13 +887,6 @@ client_intro_circ_has_opened(origin_circuit_t *circ)
log_info(LD_REND, "Introduction circuit %u has opened. Attaching streams.",
(unsigned int) TO_CIRCUIT(circ)->n_circ_id);
- /* This is an introduction circuit so we'll attach the correct
- * authentication key to the circuit identifier so it can be identified
- * properly later on. */
- if (setup_intro_circ_auth_key(circ) < 0) {
- return;
- }
-
connection_ap_attach_pending(1);
}
@@ -2047,6 +2084,7 @@ hs_client_circuit_cleanup_on_free(const circuit_t *circ)
orig_circ = CONST_TO_ORIGIN_CIRCUIT(circ);
tor_assert(orig_circ->hs_ident);
+ const ed25519_public_key_t *intro_pk = &orig_circ->hs_ident->intro_auth_pk;
has_timed_out =
(circ->marked_for_close_orig_reason == END_CIRC_REASON_TIMEOUT);
@@ -2061,22 +2099,22 @@ hs_client_circuit_cleanup_on_free(const circuit_t *circ)
safe_str_client(ed25519_fmt(&orig_circ->hs_ident->identity_pk)),
safe_str_client(build_state_get_exit_nickname(orig_circ->build_state)),
failure);
+ tor_assert_nonfatal(!ed25519_public_key_is_zero(intro_pk));
hs_cache_client_intro_state_note(&orig_circ->hs_ident->identity_pk,
- &orig_circ->hs_ident->intro_auth_pk,
- failure);
+ intro_pk, failure);
break;
case CIRCUIT_PURPOSE_C_INTRODUCING:
if (has_timed_out || !orig_circ->build_state) {
break;
}
+ tor_assert_nonfatal(!ed25519_public_key_is_zero(intro_pk));
failure = INTRO_POINT_FAILURE_UNREACHABLE;
log_info(LD_REND, "Failed v3 intro circ for service %s to intro point %s "
"(while building circuit). Marking as unreachable.",
safe_str_client(ed25519_fmt(&orig_circ->hs_ident->identity_pk)),
safe_str_client(build_state_get_exit_nickname(orig_circ->build_state)));
hs_cache_client_intro_state_note(&orig_circ->hs_ident->identity_pk,
- &orig_circ->hs_ident->intro_auth_pk,
- failure);
+ intro_pk, failure);
break;
default:
break;