aboutsummaryrefslogtreecommitdiff
path: root/src/feature/hs/hs_pow.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/feature/hs/hs_pow.c')
-rw-r--r--src/feature/hs/hs_pow.c155
1 files changed, 154 insertions, 1 deletions
diff --git a/src/feature/hs/hs_pow.c b/src/feature/hs/hs_pow.c
index 49577617e6..2e94f9bced 100644
--- a/src/feature/hs/hs_pow.c
+++ b/src/feature/hs/hs_pow.c
@@ -13,9 +13,15 @@ typedef unsigned __int128 uint128_t;
#include <stdio.h>
#include "ext/ht.h"
+#include "core/or/circuitlist.h"
+#include "core/or/origin_circuit_st.h"
+#include "feature/hs/hs_cache.h"
#include "feature/hs/hs_descriptor.h"
+#include "feature/hs/hs_client.h"
#include "feature/hs/hs_pow.h"
#include "lib/crypt_ops/crypto_rand.h"
+#include "core/mainloop/cpuworker.h"
+#include "lib/evloop/workqueue.h"
/** Replay cache set up */
/** Cache entry for (nonce, seed) replay protection. */
@@ -144,7 +150,7 @@ hs_pow_solve(const hs_pow_desc_params_t *pow_params,
/* We'll do a maximum of the nonce size iterations here which is the maximum
* number of nonce we can try in an attempt to find a valid solution. */
- log_notice(LD_REND, "Solving proof of work");
+ log_notice(LD_REND, "Solving proof of work (effort %u)", effort);
for (uint64_t i = 0; i < UINT64_MAX; i++) {
/* Calculate S = equix_solve(C || N || E) */
if (!equix_solve(ctx, challenge, HS_POW_CHALLENGE_LEN, solution)) {
@@ -290,3 +296,150 @@ hs_pow_free_service_state(hs_pow_service_state_t *state)
mainloop_event_free(state->pop_pqueue_ev);
tor_free(state);
}
+
+/* =====
+ Thread workers
+ =====*/
+
+/**
+ * An object passed to a worker thread that will try to solve the pow.
+ */
+typedef struct pow_worker_job_t {
+
+ /** Input: The pow challenge we need to solve. */
+ hs_pow_desc_params_t *pow_params;
+
+ /** State: we'll look these up to figure out how to proceed after. */
+ uint32_t intro_circ_identifier;
+ uint32_t rend_circ_identifier;
+
+ /** Output: The worker thread will malloc and write its answer here,
+ * or set it to NULL if it produced no useful answer. */
+ hs_pow_solution_t *pow_solution_out;
+
+} pow_worker_job_t;
+
+/**
+ * Worker function. This function runs inside a worker thread and receives
+ * a pow_worker_job_t as its input.
+ */
+static workqueue_reply_t
+pow_worker_threadfn(void *state_, void *work_)
+{
+ (void)state_;
+ pow_worker_job_t *job = work_;
+ job->pow_solution_out = tor_malloc_zero(sizeof(hs_pow_solution_t));
+
+ if (hs_pow_solve(job->pow_params, job->pow_solution_out)) {
+ log_info(LD_REND, "Haven't solved the PoW yet. Returning.");
+ tor_free(job->pow_solution_out);
+ job->pow_solution_out = NULL; /* how we signal that we came up empty */
+ return WQ_RPL_REPLY;
+ }
+
+ /* we have a winner! */
+ log_info(LD_REND, "cpuworker pow: we have a winner!");
+ return WQ_RPL_REPLY;
+}
+
+/**
+ * Helper: release all storage held in <b>job</b>.
+ */
+static void
+pow_worker_job_free(pow_worker_job_t *job)
+{
+ if (!job)
+ return;
+ tor_free(job->pow_params);
+ tor_free(job->pow_solution_out);
+ tor_free(job);
+}
+
+/**
+ * Worker function: This function runs in the main thread, and receives
+ * a pow_worker_job_t that the worker thread has already processed.
+ */
+static void
+pow_worker_replyfn(void *work_)
+{
+ tor_assert(in_main_thread());
+ tor_assert(work_);
+
+ pow_worker_job_t *job = work_;
+
+ // look up the circuits that we're going to use this pow in
+ origin_circuit_t *intro_circ =
+ circuit_get_by_global_id(job->intro_circ_identifier);
+ origin_circuit_t *rend_circ =
+ circuit_get_by_global_id(job->rend_circ_identifier);
+
+ /* try to re-create desc and ip */
+ const ed25519_public_key_t *service_identity_pk = NULL;
+ const hs_descriptor_t *desc = NULL;
+ const hs_desc_intro_point_t *ip = NULL;
+ if (intro_circ)
+ service_identity_pk = &intro_circ->hs_ident->identity_pk;
+ if (service_identity_pk)
+ desc = hs_cache_lookup_as_client(service_identity_pk);
+ if (desc)
+ ip = find_desc_intro_point_by_ident(intro_circ->hs_ident, desc);
+
+ if (intro_circ && rend_circ && service_identity_pk && desc && ip &&
+ job->pow_solution_out) { /* successful pow solve, and circs still here */
+
+ log_notice(LD_REND, "Got a PoW solution we like! Shipping it!");
+ /* Set flag to reflect that the HS we are attempting to rendezvous has PoW
+ * defenses enabled, and as such we will need to be more lenient with
+ * timing out while waiting for the service-side circuit to be built. */
+ rend_circ->hs_with_pow_circ = 1;
+
+ // and then send that intro cell
+ if (send_introduce1(intro_circ, rend_circ,
+ desc, job->pow_solution_out, ip) < 0) {
+ /* if it failed, mark the intro point as ready to start over */
+ intro_circ->hs_currently_solving_pow = 0;
+ }
+
+ } else { /* unsuccessful pow solve. put it back on the queue. */
+ log_notice(LD_REND,
+ "PoW cpuworker returned with no solution. Will retry soon.");
+ if (intro_circ) {
+ intro_circ->hs_currently_solving_pow = 0;
+ }
+ /* We could imagine immediately re-launching a follow-up worker
+ * here too, but for now just let the main intro loop find the
+ * not-being-serviced request and it can start everything again. For
+ * the sake of complexity, maybe that's the best long-term solution
+ * too, and we can tune the cpuworker job to try for longer if we want
+ * to improve efficiency. */
+ }
+
+ pow_worker_job_free(job);
+}
+
+/**
+ * Queue the job of solving the pow in a worker thread.
+ */
+int
+pow_queue_work(uint32_t intro_circ_identifier,
+ uint32_t rend_circ_identifier,
+ const hs_pow_desc_params_t *pow_params)
+{
+ tor_assert(in_main_thread());
+
+ pow_worker_job_t *job = tor_malloc_zero(sizeof(*job));
+ job->intro_circ_identifier = intro_circ_identifier;
+ job->rend_circ_identifier = rend_circ_identifier;
+ job->pow_params = tor_memdup(pow_params, sizeof(hs_pow_desc_params_t));
+
+ workqueue_entry_t *work;
+ work = cpuworker_queue_work(WQ_PRI_LOW,
+ pow_worker_threadfn,
+ pow_worker_replyfn,
+ job);
+ if (!work) {
+ pow_worker_job_free(job);
+ return -1;
+ }
+ return 0;
+}