Use `NoAuthenticationRequired` for DefaultCircuit
MODIFY
WalletWasabi.Tests/UnitTests/Tor/Socks5/TorTcpConnectionFactoryTests.cs
WalletWasabi.Tests/UnitTests/Tor/Socks5/TorTcpConnectionFactoryTests.cs
@@ -78,7 +78,7 @@ public class TorTcpConnectionFactoryTests
// Read SOCKS version.
int methodByte = stream.ReadByte();
- Assert.Equal(MethodField.UsernamePassword.ToByte(), methodByte);
+ Assert.Equal(MethodField.NoAuthenticationRequired.ToByte(), methodByte); // Default circuits use this MethodField.
// Write response: version + method selected.
stream.WriteByte(VerField.Socks5.Value);
@@ -116,6 +116,9 @@ public class TorTcpConnectionFactoryTests
try
{
+ // Use a person circuit to test MethodField being sent to Tor.
+ using PersonCircuit personCircuit = new();
+
// Start local TCP server.
listener = new(IPAddress.Loopback, port: 0);
listener.Start();
@@ -130,7 +133,7 @@ public class TorTcpConnectionFactoryTests
TorTcpConnectionFactory factory = new(new IPEndPoint(IPAddress.Loopback, serverPort));
Logger.LogTrace($"[{nameof(TtlExpiredScenarioAsync)}][client] About to make connection.");
- using TorTcpConnection torConnection = await factory.ConnectAsync(httpRequestHost, httpRequestPort, useSsl: false, DefaultCircuit.Instance, timeoutToken);
+ using TorTcpConnection torConnection = await factory.ConnectAsync(httpRequestHost, httpRequestPort, useSsl: false, personCircuit, timeoutToken);
Logger.LogTrace($"[{nameof(TtlExpiredScenarioAsync)}][client] Connection established.");
},
timeoutToken);

MODIFY
WalletWasabi/Tor/Socks5/TorTcpConnectionFactory.cs
WalletWasabi/Tor/Socks5/TorTcpConnectionFactory.cs
@@ -22,6 +22,9 @@ namespace WalletWasabi.Tor.Socks5;
/// </summary>
public class TorTcpConnectionFactory
{
+ private static readonly VersionMethodRequest VersionMethodNoAuthRequired = new(methods: new MethodsField(MethodField.NoAuthenticationRequired));
+ private static readonly VersionMethodRequest VersionMethodUsernamePassword = new(methods: new MethodsField(MethodField.UsernamePassword));
+
/// <param name="endPoint">Tor SOCKS5 endpoint.</param>
public TorTcpConnectionFactory(EndPoint endPoint)
{
@@ -165,19 +168,13 @@ public class TorTcpConnectionFactory
/// <exception cref="InvalidOperationException">When authentication fails due to invalid credentials.</exception>
private async Task HandshakeAsync(TcpClient tcpClient, ICircuit circuit, CancellationToken cancellationToken = default)
{
- // https://github.com/torproject/torspec/blob/master/socks-extensions.txt
- // The "NO AUTHENTICATION REQUIRED" (SOCKS5) authentication method [00] is
- // supported; and as of Tor 0.2.3.2 - alpha, the "USERNAME/PASSWORD"(SOCKS5)
- // authentication method[02] is supported too, and used as a method to
- // implement stream isolation.As an extension to support some broken clients,
- // we allow clients to pass "USERNAME/PASSWORD" authentication message to us
- // even if no authentication was selected.Furthermore, we allow
- // username / password fields of this message to be empty. This technically
- // violates RFC1929[4], but ensures interoperability with somewhat broken
- // SOCKS5 client implementations.
- MethodsField methods = new(MethodField.UsernamePassword);
-
- byte[] receiveBuffer = await SendRequestAsync(tcpClient, new VersionMethodRequest(methods), cancellationToken).ConfigureAwait(false);
+ VersionMethodRequest versionMethodRequest = circuit switch
+ {
+ DefaultCircuit => VersionMethodNoAuthRequired,
+ _ => VersionMethodUsernamePassword
+ };
+
+ byte[] receiveBuffer = await SendRequestAsync(tcpClient, versionMethodRequest, cancellationToken).ConfigureAwait(false);
MethodSelectionResponse methodSelection = new(receiveBuffer);

Use `NoAuthenticationRequired` for DefaultCircuit