diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/feature/hs/hs_pow.c | 110 | ||||
-rw-r--r-- | src/test/test_crypto.c | 10 | ||||
-rw-r--r-- | src/test/test_crypto_slow.c | 65 | ||||
-rw-r--r-- | src/test/test_sandbox.c | 27 |
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); |