summaryrefslogtreecommitdiff
path: root/src/feature/hs
diff options
context:
space:
mode:
authorMicah Elizabeth Scott <beth@torproject.org>2023-05-28 16:35:31 -0700
committerMicah Elizabeth Scott <beth@torproject.org>2023-05-28 20:02:02 -0700
commita397a92be2032e781479fa4d53a04f9b369ea1ac (patch)
treed3f35ebb5bfb99af84d911c96033ac53b28672fd /src/feature/hs
parenta3513dea54c1de6da4f5224624f545cd0d527891 (diff)
downloadtor-a397a92be2032e781479fa4d53a04f9b369ea1ac.tar.gz
tor-a397a92be2032e781479fa4d53a04f9b369ea1ac.zip
hs_pow: Update for equix API to fix issue 40794
This change adapts the hs_pow layer and unit tests to API changes in hashx and equix which modify the fault recovery responsibilities and reporting behaivor. This and the corresponding implementation changes in hashx and equix form the fix for #40794, both solving the segfault and giving hashx a way to report those failures up the call chain without them being mistaken for a different error (unusable seed) that would warrant a retry. To handle these new late compiler failures with a minimum of fuss or inefficiency, the failover is delegated to the internals of hashx and tor needs only pass in a EQUIX_CTX_TRY_COMPILE flag to get the behavior that tor was previously responsible for implementing. Signed-off-by: Micah Elizabeth Scott <beth@torproject.org>
Diffstat (limited to 'src/feature/hs')
-rw-r--r--src/feature/hs/hs_pow.c110
1 files changed, 68 insertions, 42 deletions
diff --git a/src/feature/hs/hs_pow.c b/src/feature/hs/hs_pow.c
index f75b3cb119..27d09cb0b4 100644
--- a/src/feature/hs/hs_pow.c
+++ b/src/feature/hs/hs_pow.c
@@ -27,6 +27,7 @@
#include "lib/cc/ctassert.h"
#include "core/mainloop/cpuworker.h"
#include "lib/evloop/workqueue.h"
+#include "lib/time/compat_time.h"
/** Replay cache set up */
/** Cache entry for (nonce, seed) replay protection. */
@@ -91,23 +92,6 @@ increment_and_set_nonce(uint8_t *nonce, uint8_t *challenge)
memcpy(challenge + HS_POW_NONCE_OFFSET, nonce, HS_POW_NONCE_LEN);
}
-/* Helper: Allocate an EquiX context, using the much faster compiled
- * implementation of hashx if it's available on this architecture. */
-static equix_ctx *
-build_equix_ctx(equix_ctx_flags flags)
-{
- equix_ctx *ctx = equix_alloc(flags | EQUIX_CTX_COMPILE);
- if (ctx == EQUIX_NOTSUPP) {
- ctx = equix_alloc(flags);
- }
- tor_assert_nonfatal(ctx != EQUIX_NOTSUPP);
- tor_assert_nonfatal(ctx != NULL);
- if (ctx == EQUIX_NOTSUPP) {
- ctx = NULL;
- }
- return ctx;
-}
-
/* Helper: Build EquiX challenge (P || ID || C || N || INT_32(E)) and return
* a newly allocated buffer containing it. */
static uint8_t *
@@ -214,36 +198,82 @@ hs_pow_solve(const hs_pow_solver_inputs_t *pow_inputs,
challenge = build_equix_challenge(&pow_inputs->service_blinded_id,
pow_inputs->seed, nonce, effort);
- ctx = build_equix_ctx(EQUIX_CTX_SOLVE);
+ ctx = equix_alloc(EQUIX_CTX_SOLVE | EQUIX_CTX_TRY_COMPILE);
if (!ctx) {
goto end;
}
- equix_solution solutions[EQUIX_MAX_SOLS];
- uint8_t sol_bytes[HS_POW_EQX_SOL_LEN];
+ uint8_t sol_bytes[HS_POW_EQX_SOL_LEN];
+ monotime_t start_time;
+ monotime_get(&start_time);
log_info(LD_REND, "Solving proof of work (effort %u)", effort);
+
for (;;) {
/* Calculate solutions to S = equix_solve(C || N || E), */
- int count = equix_solve(ctx, challenge, HS_POW_CHALLENGE_LEN, solutions);
- for (int i = 0; i < count; i++) {
- pack_equix_solution(&solutions[i], sol_bytes);
-
- /* Check an Equi-X solution against the effort threshold */
- if (validate_equix_challenge(challenge, sol_bytes, effort)) {
- /* Store the nonce N. */
- memcpy(pow_solution_out->nonce, nonce, HS_POW_NONCE_LEN);
- /* Store the effort E. */
- pow_solution_out->effort = effort;
- /* We only store the first 4 bytes of the seed C. */
- memcpy(pow_solution_out->seed_head, pow_inputs->seed,
+ equix_solutions_buffer buffer;
+ equix_result result;
+ result = equix_solve(ctx, challenge, HS_POW_CHALLENGE_LEN, &buffer);
+ switch (result) {
+
+ case EQUIX_OK:
+ for (unsigned i = 0; i < buffer.count; i++) {
+ pack_equix_solution(&buffer.sols[i], sol_bytes);
+
+ /* Check an Equi-X solution against the effort threshold */
+ if (validate_equix_challenge(challenge, sol_bytes, effort)) {
+ /* Store the nonce N. */
+ memcpy(pow_solution_out->nonce, nonce, HS_POW_NONCE_LEN);
+ /* Store the effort E. */
+ pow_solution_out->effort = effort;
+ /* We only store the first 4 bytes of the seed C. */
+ memcpy(pow_solution_out->seed_head, pow_inputs->seed,
sizeof(pow_solution_out->seed_head));
- /* Store the solution S */
- memcpy(&pow_solution_out->equix_solution, sol_bytes, sizeof sol_bytes);
+ /* Store the solution S */
+ memcpy(&pow_solution_out->equix_solution,
+ sol_bytes, sizeof sol_bytes);
+
+ monotime_t end_time;
+ monotime_get(&end_time);
+ int64_t duration_usec = monotime_diff_usec(&start_time, &end_time);
+ log_info(LD_REND, "Proof of work solution (effort %u) found "
+ "using %s implementation in %u.%06u seconds",
+ effort,
+ (EQUIX_SOLVER_DID_USE_COMPILER & buffer.flags)
+ ? "compiled" : "interpreted",
+ (unsigned)(duration_usec / 1000000),
+ (unsigned)(duration_usec % 1000000));
+
+ /* Indicate success and we are done. */
+ ret = 0;
+ goto end;
+ }
+ }
+ break;
+
+ case EQUIX_FAIL_CHALLENGE:
+ /* This happens occasionally due to HashX rejecting some program
+ * configurations. For our purposes here it's the same as count==0.
+ * Increment the nonce and try again. */
+ break;
+
+ case EQUIX_FAIL_COMPILE:
+ /* The interpreter is disabled and the compiler failed */
+ log_warn(LD_REND, "Proof of work solver failed, "
+ "compile error with no fallback enabled.");
+ goto end;
- /* Indicate success and we are done. */
- ret = 0;
+ /* These failures are not applicable to equix_solve, but included for
+ * completeness and to satisfy exhaustive enum warnings. */
+ case EQUIX_FAIL_ORDER:
+ case EQUIX_FAIL_PARTIAL_SUM:
+ case EQUIX_FAIL_FINAL_SUM:
+ /* And these really should not happen, and indicate
+ * programming errors if they do. */
+ case EQUIX_FAIL_NO_SOLVER:
+ case EQUIX_FAIL_INTERNAL:
+ default:
+ tor_assert_nonfatal_unreached();
goto end;
- }
}
/* No solutions for this nonce and/or none that passed the effort
@@ -309,7 +339,7 @@ hs_pow_verify(const ed25519_public_key_t *service_blinded_id,
goto done;
}
- ctx = build_equix_ctx(EQUIX_CTX_VERIFY);
+ ctx = equix_alloc(EQUIX_CTX_VERIFY | EQUIX_CTX_TRY_COMPILE);
if (!ctx) {
goto done;
}
@@ -401,11 +431,7 @@ pow_worker_threadfn(void *state_, void *work_)
log_warn(LD_REND, "Failed to run the proof of work solver");
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 has a proof of work solution");
return WQ_RPL_REPLY;
}