Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DRAFT: tor proxy #65

Draft
wants to merge 8 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,21 +59,6 @@ jobs:
dotnet fantomless --recurse .
git diff --exit-code

- name: Install dependencies of commitlint
run: |
sudo apt update
sudo apt install --yes git npm
- name: Pull our commitlint configuration
run: |
git clone https://github.com/nblockchain/conventions.git
rm -rf ./conventions/.git/
- name: Validate current commit (last commit) with commitlint
if: github.event_name == 'push'
run: ./conventions/commitlint.sh --from HEAD~1 --to HEAD --verbose
- name: Validate PR commits with commitlint
if: github.event_name == 'pull_request'
run: ./conventions/commitlint.sh --from ${{ github.event.pull_request.head.sha }}~${{ github.event.pull_request.commits }} --to ${{ github.event.pull_request.head.sha }} --verbose

package:
name: Package (Nuget)
needs: sanity_check
Expand Down
2 changes: 1 addition & 1 deletion NOnion.Tests/FallbackDirectorySelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ static internal IPEndPoint GetRandomFallbackDirectory()
{
if (fallbackDirectories == null)
{
var urlToTorServerList = "https://raw.githubusercontent.com/torproject/tor/main/src/app/config/fallback_dirs.inc";
var urlToTorServerList = "https://gitlab.torproject.org/tpo/core/tor/-/raw/main/src/app/config/fallback_dirs.inc";
using var webClient = new WebClient();
var fetchedInfo = webClient.DownloadString(urlToTorServerList);

Expand Down
62 changes: 36 additions & 26 deletions NOnion.Tests/HiddenServicesTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using NOnion.Network;
using NOnion.Http;
using NOnion.Cells.Relay;
using NOnion.Client;
using NOnion.Directory;
using NOnion.Tests.Utility;
using NOnion.Services;
Expand All @@ -24,6 +25,23 @@ namespace NOnion.Tests
{
public class HiddenServicesTests
{
[OneTimeSetUp]
public void Init()
{
cachePath =
new DirectoryInfo(
Path.Combine(
Path.GetTempPath(),
Path.GetFileNameWithoutExtension(
Path.GetRandomFileName()
)
)
);
cachePath.Create();
}

private DirectoryInfo cachePath = null;

/* It's possible that the router returned by GetRandomFallbackDirectory or
* GetRandomRoutersForDirectoryBrowsing be inaccessable so we need to continue
* retrying if an exceptions happened to make sure the issues are not related
Expand All @@ -33,11 +51,8 @@ public class HiddenServicesTests

private async Task CreateIntroductionCircuit()
{
var node = (CircuitNodeDetail.Create)(await CircuitHelper.GetRandomRoutersForDirectoryBrowsingWithRetry()).First();
using TorGuard guard = await TorGuard.NewClientAsync(node.EndPoint);
var circuit = new TorCircuit(guard);

await circuit.CreateAsync(CircuitNodeDetail.FastCreate);
using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);
var circuit = await torClient.CreateCircuitAsync(1, CircuitPurpose.Unknown, FSharpOption<CircuitNodeDetail>.None);
await circuit.RegisterAsIntroductionPointAsync(FSharpOption<AsymmetricCipherKeyPair>.None, StubCallback, DisconnectionCallback);
}

Expand All @@ -61,12 +76,8 @@ private async Task CreateRendezvousCircuit()
var array = new byte[Constants.RendezvousCookieLength];
RandomNumberGenerator.Create().GetNonZeroBytes(array);

var nodes = await CircuitHelper.GetRandomRoutersForDirectoryBrowsingWithRetry(2);
using TorGuard guard = await TorGuard.NewClientAsync(((CircuitNodeDetail.Create)nodes[0]).EndPoint);
var circuit = new TorCircuit(guard);

await circuit.CreateAsync(nodes[0]);
await circuit.ExtendAsync(nodes[1]);
using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);
var circuit = await torClient.CreateCircuitAsync(2, CircuitPurpose.Unknown, FSharpOption<CircuitNodeDetail>.None);
await circuit.RegisterAsRendezvousPointAsync(array);
}

Expand All @@ -92,10 +103,11 @@ private async Task<int> ReadExact(TorStream stream, byte[] buffer, int off, int

public async Task BrowseFacebookOverHS()
{
TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath()));
using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);

var client = await TorServiceClient.ConnectAsync(directory, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
var httpClient = new TorHttpClient(client.GetStream(), "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
var serviceClient = await TorServiceClient.ConnectAsync(torClient, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
var stream = await serviceClient.GetStreamAsync();
var httpClient = new TorHttpClient(stream, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");

try
{
Expand All @@ -117,11 +129,11 @@ public void CanBrowseFacebookOverHS()

public async Task BrowseFacebookOverHSWithTLS()
{
TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath()));

var client = await TorServiceClient.ConnectAsync(directory, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:443");

var sslStream = new SslStream(client.GetStream(), true, (sender, cert, chain, sslPolicyErrors) => true);
using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);
var serviceClient = await TorServiceClient.ConnectAsync(torClient, "facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion:443");
var stream = await serviceClient.GetStreamAsync();
var sslStream = new SslStream(stream, true, (sender, cert, chain, sslPolicyErrors) => true);
await sslStream.AuthenticateAsClientAsync(string.Empty, null, SslProtocols.Tls12, false);

var httpClientOverSslStream = new TorHttpClient(sslStream, "www.facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
Expand All @@ -147,18 +159,16 @@ public void CanBrowseFacebookOverHSWithTLS()

public async Task EstablishAndCommunicateOverHSConnectionOnionStyle()
{
int descriptorUploadRetryLimit = 2;

TorDirectory directory = await TorDirectory.BootstrapAsync(FallbackDirectorySelector.GetRandomFallbackDirectory(), new DirectoryInfo(Path.GetTempPath()));

using TorClient torClient = await TorClient.BootstrapWithGitlabAsync(cachePath);

TorLogger.Log("Finished bootstraping");

SecureRandom random = new SecureRandom();
Ed25519KeyPairGenerator kpGen = new Ed25519KeyPairGenerator();
kpGen.Init(new Ed25519KeyGenerationParameters(random));
Ed25519PrivateKeyParameters masterPrivateKey = (Ed25519PrivateKeyParameters)kpGen.GenerateKeyPair().Private;

TorServiceHost host = new TorServiceHost(directory, descriptorUploadRetryLimit, TestsRetryCount, FSharpOption<Ed25519PrivateKeyParameters>.Some(masterPrivateKey));
TorServiceHost host = new TorServiceHost(torClient, FSharpOption<Ed25519PrivateKeyParameters>.Some(masterPrivateKey));
await host.StartAsync();

TorLogger.Log("Finished starting HS host");
Expand All @@ -175,8 +185,8 @@ public async Task EstablishAndCommunicateOverHSConnectionOnionStyle()

var clientSide =
Task.Run(async () => {
var client = await TorServiceClient.ConnectAsync(directory, host.ExportUrl());
var stream = client.GetStream();
var serviceClient = await TorServiceClient.ConnectAsync(torClient, host.ExportUrl());
var stream = await serviceClient.GetStreamAsync();
var lengthBytes = new byte[sizeof(int)];
await ReadExact(stream, lengthBytes, 0, lengthBytes.Length);
var length = BitConverter.ToInt32(lengthBytes);
Expand Down
47 changes: 47 additions & 0 deletions NOnion.Tests/TorClientTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using Microsoft.FSharp.Core;
using System.IO;
using System.Threading.Tasks;

using NUnit.Framework;

using NOnion.Client;

namespace NOnion.Tests
{
public class TorClientTests
{
private async Task BootstrapWithGitlab()
{
await TorClient.BootstrapWithGitlabAsync(FSharpOption<DirectoryInfo>.None);
}

[Test]
public void CanBootstrapWithGitlab()
{
Assert.DoesNotThrowAsync(BootstrapWithGitlab);
}

private async Task BootstrapWithEmbeddedList()
{
await TorClient.BootstrapWithEmbeddedListAsync(FSharpOption<DirectoryInfo>.None);
}

[Test]
public void CanBootstrapWithEmbeddedList()
{
Assert.DoesNotThrowAsync(BootstrapWithEmbeddedList);
}

private async Task CreateCircuit()
{
using TorClient client = await TorClient.BootstrapWithEmbeddedListAsync(FSharpOption<DirectoryInfo>.None);
await client.CreateCircuitAsync(3, CircuitPurpose.Unknown, FSharpOption<Network.CircuitNodeDetail>.None);
}

[Test]
public void CanCreateCircuit()
{
Assert.DoesNotThrowAsync(CreateCircuit);
}
}
}
115 changes: 115 additions & 0 deletions NOnion.Tests/TorProxyTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

using Newtonsoft.Json;
using NUnit.Framework;

using NOnion.Proxy;

namespace NOnion.Tests
{
internal class TorProxyTests
{
private const int MaximumRetry = 3;

private class TorProjectCheckResult
{
[JsonProperty("IsTor")]
internal bool IsTor { get; set; }

[JsonProperty("IP")]
internal string IP { get; set; }
}

[Test]
[Retry(MaximumRetry)]
public void CanProxyTorProjectExitNodeCheck()
{
Assert.DoesNotThrowAsync(ProxyTorProjectExitNodeCheck);
}

private async Task ProxyTorProjectExitNodeCheck()
{
using (await TorProxy.StartAsync(IPAddress.Loopback, 20000))
{
var handler = new HttpClientHandler
{
Proxy = new WebProxy("http://localhost:20000")
};

var client = new HttpClient(handler);
var resultStr = await client.GetStringAsync("https://check.torproject.org/api/ip");
var result = JsonConvert.DeserializeObject<TorProjectCheckResult>(resultStr);
Assert.IsTrue(result.IsTor);
}
}

[Test]
[Retry(MaximumRetry)]
public void CanProxyHttps()
{
Assert.DoesNotThrowAsync(ProxyHttps);
}

private async Task ProxyHttps()
{
using (await TorProxy.StartAsync(IPAddress.Loopback, 20000))
{
var handler = new HttpClientHandler
{
Proxy = new WebProxy("http://localhost:20000")
};

var client = new HttpClient(handler);
var googleResponse = await client.GetAsync("https://google.com");
Assert.That(googleResponse.StatusCode > 0);
}
}

[Test]
[Retry(MaximumRetry)]
public void CanProxyHttp()
{
Assert.DoesNotThrowAsync(ProxyHttp);
}

private async Task ProxyHttp()
{
using (await TorProxy.StartAsync(IPAddress.Loopback, 20000))
{
var handler = new HttpClientHandler
{
Proxy = new WebProxy("http://localhost:20000")
};

var client = new HttpClient(handler);
var googleResponse = await client.GetAsync("http://google.com/search?q=Http+Test");
Assert.That(googleResponse.StatusCode > 0);
}
}

[Test]
[Retry(MaximumRetry)]
public void CanProxyHiddenService()
{
Assert.DoesNotThrowAsync(ProxyHiddenService);
}

private async Task ProxyHiddenService()
{
using (await TorProxy.StartAsync(IPAddress.Loopback, 20000))
{
var handler = new HttpClientHandler
{
Proxy = new WebProxy("http://localhost:20000"),
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => true
};

var client = new HttpClient(handler);
var facebookResponse = await client.GetAsync("https://facebookwkhpilnemxj7asaniu7vnjjbiltxjqhye3mhbshg7kx5tfyd.onion");
Assert.That(facebookResponse.StatusCode > 0);
}
}
}
}
Loading
Loading