aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/feature/hs/hs_pow.c110
-rw-r--r--src/test/test_crypto.c10
-rw-r--r--src/test/test_crypto_slow.c65
-rw-r--r--src/test/test_sandbox.c27
4 files changed, 117 insertions, 95 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;
}
diff --git a/src/test/test_crypto.c b/src/test/test_crypto.c
index 926d4178c1..db5a2db650 100644
--- a/src/test/test_crypto.c
+++ b/src/test/test_crypto.c
@@ -2982,9 +2982,9 @@ test_crypto_hashx(void *arg)
static const struct {
hashx_type type;
} variations[] = {
- { HASHX_INTERPRETED },
+ { HASHX_TYPE_INTERPRETED },
#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__)
- { HASHX_COMPILED },
+ { HASHX_TYPE_COMPILED },
#endif
};
@@ -3013,12 +3013,12 @@ test_crypto_hashx(void *arg)
ctx = hashx_alloc(variations[vari_i].type);
tt_ptr_op(ctx, OP_NE, NULL);
- tt_ptr_op(ctx, OP_NE, HASHX_NOTSUPP);
retval = hashx_make(ctx, seed_literal, seed_len);
- tt_int_op(retval, OP_EQ, 1);
+ tt_int_op(retval, OP_EQ, HASHX_OK);
memset(out_actual, 0xa5, sizeof out_actual);
- hashx_exec(ctx, hash_input, out_actual);
+ retval = hashx_exec(ctx, hash_input, out_actual);
+ tt_int_op(retval, OP_EQ, HASHX_OK);
tt_mem_op(out_actual, OP_EQ, out_expected, sizeof out_actual);
}
}
diff --git a/src/test/test_crypto_slow.c b/src/test/test_crypto_slow.c
index 5ffd199813..23bc7a852f 100644
--- a/src/test/test_crypto_slow.c
+++ b/src/test/test_crypto_slow.c
@@ -636,14 +636,21 @@ test_crypto_equix(void *arg)
static const struct {
equix_ctx_flags flags;
equix_result expected;
+ equix_solution_flags sol_flags;
} variations[] = {
- {0, EQUIX_OK},
- {0, EQUIX_ORDER},
- {0, EQUIX_PARTIAL_SUM},
+ {0, EQUIX_OK, 0},
+ {0, EQUIX_FAIL_ORDER, 0},
+ {0, EQUIX_FAIL_PARTIAL_SUM, 0},
#if defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__)
- {EQUIX_CTX_COMPILE, EQUIX_OK},
- {EQUIX_CTX_COMPILE, EQUIX_ORDER},
- {EQUIX_CTX_COMPILE, EQUIX_PARTIAL_SUM},
+ { EQUIX_CTX_MUST_COMPILE, EQUIX_OK,
+ EQUIX_SOLVER_DID_USE_COMPILER
+ },
+ { EQUIX_CTX_MUST_COMPILE, EQUIX_FAIL_ORDER,
+ EQUIX_SOLVER_DID_USE_COMPILER
+ },
+ { EQUIX_CTX_MUST_COMPILE, EQUIX_FAIL_PARTIAL_SUM,
+ EQUIX_SOLVER_DID_USE_COMPILER
+ },
#endif
};
@@ -659,48 +666,42 @@ test_crypto_equix(void *arg)
for (unsigned vari_i = 0; vari_i < num_variations; vari_i++) {
const equix_ctx_flags flags = variations[vari_i].flags;
+ const equix_solution_flags sol_flags = variations[vari_i].sol_flags;
const equix_result expected = variations[vari_i].expected;
- equix_solution sols_actual[EQUIX_MAX_SOLS];
+ equix_solutions_buffer output;
equix_ctx *solve_ctx = NULL, *verify_ctx = NULL;
solve_ctx = equix_alloc(EQUIX_CTX_SOLVE | flags);
tt_ptr_op(solve_ctx, OP_NE, NULL);
- tt_ptr_op(solve_ctx, OP_NE, EQUIX_NOTSUPP);
/* Solve phase: Make sure the test vector matches */
- memset(sols_actual, 0xa5, sizeof sols_actual);
- int retval = equix_solve(solve_ctx, challenge_literal,
- challenge_len, sols_actual);
- tt_int_op(retval, OP_EQ, num_sols);
- tt_mem_op(sols_actual, OP_EQ, sols_expected,
+ memset(&output, 0xa5, sizeof output);
+ equix_result result;
+ result = equix_solve(solve_ctx, challenge_literal,
+ challenge_len, &output);
+ tt_int_op(result, OP_EQ, EQUIX_OK);
+ tt_int_op(output.count, OP_EQ, num_sols);
+ tt_int_op(output.flags, OP_EQ, sol_flags);
+ tt_mem_op(output.sols, OP_EQ, sols_expected,
num_sols * sizeof(equix_solution));
verify_ctx = equix_alloc(EQUIX_CTX_VERIFY | flags);
tt_ptr_op(verify_ctx, OP_NE, NULL);
- tt_ptr_op(verify_ctx, OP_NE, EQUIX_NOTSUPP);
/* Use each solution for positive and negative tests of verify */
for (size_t sol_i = 0; sol_i < num_sols; sol_i++) {
- equix_result result;
equix_idx tmp_idx;
- equix_solution *sol = &sols_actual[sol_i];
-
- switch (expected) {
- case EQUIX_ORDER:
- /* Swap two otherwise valid indices, to trigger an order error */
- tmp_idx = sol->idx[0];
- sol->idx[0] = sol->idx[1];
- sol->idx[1] = tmp_idx;
- break;
- case EQUIX_FINAL_SUM:
- case EQUIX_PARTIAL_SUM:
- /* Most changes to the solution will cause a partial sum error */
- sol->idx[0]++;
- break;
- case EQUIX_OK:
- case EQUIX_CHALLENGE:
- break;
+ equix_solution *sol = &output.sols[sol_i];
+
+ if (expected == EQUIX_FAIL_ORDER) {
+ /* Swap two otherwise valid indices, to trigger an order error */
+ tmp_idx = sol->idx[0];
+ sol->idx[0] = sol->idx[1];
+ sol->idx[1] = tmp_idx;
+ } else if (expected == EQUIX_FAIL_PARTIAL_SUM) {
+ /* Most changes to the solution will cause a partial sum error */
+ sol->idx[0]++;
}
result = equix_verify(verify_ctx, challenge_literal,
diff --git a/src/test/test_sandbox.c b/src/test/test_sandbox.c
index a28c9b6e41..64182ecc91 100644
--- a/src/test/test_sandbox.c
+++ b/src/test/test_sandbox.c
@@ -315,32 +315,27 @@ test_sandbox_crypto_equix(void *arg)
{{ 0x62c5, 0x86d1, 0x5752, 0xe1f0, 0x12da, 0x8f33, 0x7336, 0xf161 }},
};
- equix_solution sols_actual[EQUIX_MAX_SOLS] = { 0 };
+ equix_solutions_buffer output;
equix_ctx *solve_ctx = NULL, *verify_ctx = NULL;
- /* TODO: A subsequent change will modify these flags to use an auto fallback
- * that will be built into our fork of equix. (This implements a
- * performant and low-complexity way to share the generated program
- * state during fallback instead of re-generating it.)
- */
- solve_ctx = equix_alloc(EQUIX_CTX_SOLVE | EQUIX_CTX_COMPILE);
+ solve_ctx = equix_alloc(EQUIX_CTX_SOLVE | EQUIX_CTX_TRY_COMPILE);
tt_ptr_op(solve_ctx, OP_NE, NULL);
- tt_ptr_op(solve_ctx, OP_NE, EQUIX_NOTSUPP);
- int retval = equix_solve(solve_ctx, challenge_literal,
- challenge_len, sols_actual);
- tt_int_op(retval, OP_EQ, num_sols);
- tt_mem_op(sols_actual, OP_EQ, sols_expected,
+ equix_result result;
+ memset(&output, 0xEE, sizeof output);
+ result = equix_solve(solve_ctx, challenge_literal, challenge_len, &output);
+ tt_int_op(result, OP_EQ, EQUIX_OK);
+ tt_int_op(output.count, OP_EQ, num_sols);
+ tt_int_op(output.flags, OP_EQ, 0); /* EQUIX_SOLVER_DID_USE_COMPILER unset */
+ tt_mem_op(output.sols, OP_EQ, sols_expected,
num_sols * sizeof(equix_solution));
- verify_ctx = equix_alloc(EQUIX_CTX_VERIFY | EQUIX_CTX_COMPILE);
+ verify_ctx = equix_alloc(EQUIX_CTX_VERIFY | EQUIX_CTX_TRY_COMPILE);
tt_ptr_op(verify_ctx, OP_NE, NULL);
- tt_ptr_op(verify_ctx, OP_NE, EQUIX_NOTSUPP);
/* Test one of the solutions randomly */
- equix_result result;
const unsigned sol_i = crypto_rand_int(num_sols);
- equix_solution *sol = &sols_actual[sol_i];
+ equix_solution *sol = &output.sols[sol_i];
result = equix_verify(verify_ctx, challenge_literal,
challenge_len, sol);