tor: enable PoW defenses for automatically created hidden services

Enable PoW defenses [1] for hidden services that we create via
Tor Control using the `ADD_ONION` command [2].

The ability to do that has been added in tor-0.4.9.2-alpha [3]. Previous
versions return a syntax error to the `ADD_ONION` command with
`PoWDefensesEnabled=1`, so the approach here is to try with PoW and if
we get syntax error, then retry without PoW.

[1] https://tpo.pages.torproject.net/onion-services/ecosystem/technology/security/pow/
[2] https://spec.torproject.org/control-spec/commands.html#add_onion
[3] 02c1804446
This commit is contained in:
Vasil Dimov
2025-09-15 15:44:03 +02:00
parent fb993f7604
commit 4c6798a3d3
3 changed files with 31 additions and 7 deletions

View File

@@ -54,6 +54,9 @@ FUZZ_TARGET(torcontrol, .init = initialize_torcontrol)
[&] {
tor_control_reply.code = TOR_REPLY_UNRECOGNIZED;
},
[&] {
tor_control_reply.code = TOR_REPLY_SYNTAX_ERROR;
},
[&] {
tor_control_reply.code = fuzzed_data_provider.ConsumeIntegral<int>();
});
@@ -65,7 +68,10 @@ FUZZ_TARGET(torcontrol, .init = initialize_torcontrol)
CallOneOf(
fuzzed_data_provider,
[&] {
tor_controller.add_onion_cb(dummy_tor_control_connection, tor_control_reply);
tor_controller.add_onion_cb(dummy_tor_control_connection, tor_control_reply, /*pow_was_enabled=*/true);
},
[&] {
tor_controller.add_onion_cb(dummy_tor_control_connection, tor_control_reply, /*pow_was_enabled=*/false);
},
[&] {
tor_controller.auth_cb(dummy_tor_control_connection, tor_control_reply);

View File

@@ -423,10 +423,20 @@ void TorController::get_socks_cb(TorControlConnection& _conn, const TorControlRe
}
}
void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply)
static std::string MakeAddOnionCmd(const std::string& private_key, const std::string& target, bool enable_pow)
{
// Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports.
return strprintf("ADD_ONION %s%s Port=%i,%s",
private_key,
enable_pow ? " PoWDefensesEnabled=1" : "",
Params().GetDefaultPort(),
target);
}
void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlReply& reply, bool pow_was_enabled)
{
if (reply.code == TOR_REPLY_OK) {
LogDebug(BCLog::TOR, "ADD_ONION successful\n");
LogDebug(BCLog::TOR, "ADD_ONION successful (PoW defenses %s)", pow_was_enabled ? "enabled" : "disabled");
for (const std::string &s : reply.lines) {
std::map<std::string,std::string> m = ParseTorReplyMapping(s);
std::map<std::string,std::string>::iterator i;
@@ -453,6 +463,12 @@ void TorController::add_onion_cb(TorControlConnection& _conn, const TorControlRe
// ... onion requested - keep connection open
} else if (reply.code == TOR_REPLY_UNRECOGNIZED) {
LogWarning("tor: Add onion failed with unrecognized command (You probably need to upgrade Tor)");
} else if (pow_was_enabled && reply.code == TOR_REPLY_SYNTAX_ERROR) {
LogDebug(BCLog::TOR, "ADD_ONION failed with PoW defenses, retrying without");
_conn.Command(MakeAddOnionCmd(private_key, m_target.ToStringAddrPort(), /*enable_pow=*/false),
[this](TorControlConnection& conn, const TorControlReply& reply) {
add_onion_cb(conn, reply, /*pow_was_enabled=*/false);
});
} else {
LogWarning("tor: Add onion failed; error code %d", reply.code);
}
@@ -474,9 +490,10 @@ void TorController::auth_cb(TorControlConnection& _conn, const TorControlReply&
private_key = "NEW:ED25519-V3"; // Explicitly request key type - see issue #9214
}
// Request onion service, redirect port.
// Note that the 'virtual' port is always the default port to avoid decloaking nodes using other ports.
_conn.Command(strprintf("ADD_ONION %s Port=%i,%s", private_key, Params().GetDefaultPort(), m_target.ToStringAddrPort()),
std::bind_front(&TorController::add_onion_cb, this));
_conn.Command(MakeAddOnionCmd(private_key, m_target.ToStringAddrPort(), /*enable_pow=*/true),
[this](TorControlConnection& conn, const TorControlReply& reply) {
add_onion_cb(conn, reply, /*pow_was_enabled=*/true);
});
} else {
LogWarning("tor: Authentication failed");
}

View File

@@ -27,6 +27,7 @@ static const bool DEFAULT_LISTEN_ONION = true;
/** Tor control reply code. Ref: https://spec.torproject.org/control-spec/replies.html */
constexpr int TOR_REPLY_OK{250};
constexpr int TOR_REPLY_UNRECOGNIZED{510};
constexpr int TOR_REPLY_SYNTAX_ERROR{512}; //!< Syntax error in command argument
void StartTorControl(CService onion_service_target);
void InterruptTorControl();
@@ -142,7 +143,7 @@ public:
/** Callback for GETINFO net/listeners/socks result */
void get_socks_cb(TorControlConnection& conn, const TorControlReply& reply);
/** Callback for ADD_ONION result */
void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply);
void add_onion_cb(TorControlConnection& conn, const TorControlReply& reply, bool pow_was_enabled);
/** Callback for AUTHENTICATE result */
void auth_cb(TorControlConnection& conn, const TorControlReply& reply);
/** Callback for AUTHCHALLENGE result */