diff --git a/doc/man/tor.1.txt b/doc/man/tor.1.txt index 19fdf90c90..1589809b1a 100644 --- a/doc/man/tor.1.txt +++ b/doc/man/tor.1.txt @@ -3119,6 +3119,21 @@ The following options are per onion service: The maximum burst size for rendezvous requests handled from the priority queue at once. (Default: 2500) +These options are applicable to both onion services and their clients: + +[[CompiledProofOfWorkHash]] **CompiledProofOfWorkHash** **0**|**1**|**auto**:: + When proof-of-work DoS mitigation is active, both the services themselves + and the clients which connect will use a dynamically generated hash + function as part of the puzzle computation. + + + If this option is set to 1, puzzles will only be solved and verified using + the compiled implementation (about 20x faster) and we choose to fail rather + than using a slower fallback. If it's 0, the compiler will never be used. + By default, the compiler is always tried if possible but the interpreter is + available as a fallback. (Default: auto) + +See also <>, these proof of work options +have no effect unless the "`pow`" module is enabled at compile time. == DIRECTORY AUTHORITY SERVER OPTIONS diff --git a/src/app/config/config.c b/src/app/config/config.c index 10090f273d..4a703abaa3 100644 --- a/src/app/config/config.c +++ b/src/app/config/config.c @@ -380,6 +380,7 @@ static const config_var_t option_vars_[] = { V(ClientTransportPlugin, LINELIST, NULL), V(ClientUseIPv6, BOOL, "1"), V(ClientUseIPv4, BOOL, "1"), + V(CompiledProofOfWorkHash, AUTOBOOL, "auto"), V(ConfluxEnabled, AUTOBOOL, "auto"), VAR("ConfluxClientUX", STRING, ConfluxClientUX_option, "throughput"), diff --git a/src/app/config/or_options_st.h b/src/app/config/or_options_st.h index c8680bb49e..36b00662b5 100644 --- a/src/app/config/or_options_st.h +++ b/src/app/config/or_options_st.h @@ -723,6 +723,11 @@ struct or_options_t { * accessing this value directly. */ int ClientPreferIPv6DirPort; + /** If true, always use the compiled hash implementation. If false, always + * the interpreter. Default of "auto" allows a dynamic fallback from + * copmiler to interpreter. */ + int CompiledProofOfWorkHash; + /** If true, the tor client will use conflux for its general purpose * circuits which excludes onion service traffic. */ int ConfluxEnabled; diff --git a/src/feature/hs/hs_client.c b/src/feature/hs/hs_client.c index 0e4b3ca0c3..2bb59f078e 100644 --- a/src/feature/hs/hs_client.c +++ b/src/feature/hs/hs_client.c @@ -750,7 +750,8 @@ consider_sending_introduce1(origin_circuit_t *intro_circ, */ if (have_module_pow() && desc->encrypted_data.pow_params) { hs_pow_solver_inputs_t pow_inputs = { - .effort = desc->encrypted_data.pow_params->suggested_effort + .effort = desc->encrypted_data.pow_params->suggested_effort, + .CompiledProofOfWorkHash = get_options()->CompiledProofOfWorkHash }; ed25519_pubkey_copy(&pow_inputs.service_blinded_id, &desc->plaintext_data.blinded_pubkey); diff --git a/src/feature/hs/hs_pow.c b/src/feature/hs/hs_pow.c index 27d09cb0b4..5cee3b00d7 100644 --- a/src/feature/hs/hs_pow.c +++ b/src/feature/hs/hs_pow.c @@ -175,9 +175,24 @@ unpack_equix_solution(const uint8_t *bytes_in, } } +/** Helper: Map the CompiledProofOfWorkHash configuration option to its + * corresponding equix_ctx_flags bit. */ +static equix_ctx_flags +hs_pow_equix_option_flags(int CompiledProofOfWorkHash) +{ + if (CompiledProofOfWorkHash == 0) { + return 0; + } else if (CompiledProofOfWorkHash == 1) { + return EQUIX_CTX_MUST_COMPILE; + } else { + tor_assert_nonfatal(CompiledProofOfWorkHash == -1); + return EQUIX_CTX_TRY_COMPILE; + } +} + /** Solve the EquiX/blake2b PoW scheme using the parameters in pow_params, and * store the solution in pow_solution_out. Returns 0 on success and -1 - * otherwise. Called by a client. */ + * otherwise. Called by a client, from a cpuworker thread. */ int hs_pow_solve(const hs_pow_solver_inputs_t *pow_inputs, hs_pow_solution_t *pow_solution_out) @@ -198,7 +213,10 @@ 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 = equix_alloc(EQUIX_CTX_SOLVE | EQUIX_CTX_TRY_COMPILE); + /* This runs on a cpuworker, let's not access global get_options(). + * Instead, the particular options we need are captured in pow_inputs. */ + ctx = equix_alloc(EQUIX_CTX_SOLVE | + hs_pow_equix_option_flags(pow_inputs->CompiledProofOfWorkHash)); if (!ctx) { goto end; } @@ -339,7 +357,8 @@ hs_pow_verify(const ed25519_public_key_t *service_blinded_id, goto done; } - ctx = equix_alloc(EQUIX_CTX_VERIFY | EQUIX_CTX_TRY_COMPILE); + ctx = equix_alloc(EQUIX_CTX_VERIFY | + hs_pow_equix_option_flags(get_options()->CompiledProofOfWorkHash)); if (!ctx) { goto done; } @@ -428,7 +447,6 @@ pow_worker_threadfn(void *state_, void *work_) job->pow_solution_out = tor_malloc_zero(sizeof(hs_pow_solution_t)); if (hs_pow_solve(&job->pow_inputs, job->pow_solution_out)) { - 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 */ } diff --git a/src/feature/hs/hs_pow.h b/src/feature/hs/hs_pow.h index b5949b7916..d47eba82ab 100644 --- a/src/feature/hs/hs_pow.h +++ b/src/feature/hs/hs_pow.h @@ -84,6 +84,8 @@ typedef struct hs_pow_solver_inputs_t { /** Effort chosen by the client. May be higher or lower than * suggested_effort in the descriptor. */ uint32_t effort; + /** Configuration option, choice of hash implementation. AUTOBOOL. */ + int CompiledProofOfWorkHash; } hs_pow_solver_inputs_t; /** State and parameters of PoW defenses, stored in the service state. */ diff --git a/src/test/test_hs_pow_slow.c b/src/test/test_hs_pow_slow.c index e21eee3395..ff715cf53e 100644 --- a/src/test/test_hs_pow_slow.c +++ b/src/test/test_hs_pow_slow.c @@ -218,6 +218,7 @@ test_hs_pow_vectors(void *arg) hs_pow_solution_t solution = { 0 }; hs_pow_solver_inputs_t input = { .effort = vectors[vec_i].effort, + .CompiledProofOfWorkHash = -1 }; tt_int_op(strlen(service_blinded_id_hex), OP_EQ, 2 * HS_POW_ID_LEN);