8081d98c67
Initial public release. Anonymous on-device identity (no phone number, e-mail, or password), Signal-protocol cryptography (X3DH + PQXDH, Double Ratchet, sealed sender) via libsignal, a metadata-minimizing relay, group chats, disappearing messages, encrypted local storage, optional Tor transport, and a UI localized into Czech, English, German, Russian, and Italian. Monorepo: Flutter client (app/), Rust relay + wire types (rust/), protobuf definitions (proto/), self-host deploy (deploy/). Licensed under AGPL-3.0 with an app-store distribution exception (§7).
87 lines
3.0 KiB
Dart
87 lines
3.0 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:sepot/core/network/relay_transport.dart';
|
|
import 'package:socks5_proxy/socks_server.dart';
|
|
|
|
/// Deterministické ověření [TorTransport]: REST i WS musí projít lokálním
|
|
/// SOCKS5 serverem k lokálnímu cílovému serveru. Žádná síť ven, žádný
|
|
/// libsignal — jen transportní vrstva. Doplňuje živý smoke proti onion
|
|
/// (`tor_transport_live_test.dart`, tag `tor`).
|
|
void main() {
|
|
group('TorTransport přes lokální SOCKS5 proxy', () {
|
|
late SocksServer proxy;
|
|
late int proxyPort;
|
|
late HttpServer target;
|
|
late int targetPort;
|
|
|
|
setUp(() async {
|
|
// SOCKS5 proxy, která každý požadavek prostě forwarduje na cíl.
|
|
proxy = SocksServer();
|
|
proxy.connections.listen((c) async {
|
|
try {
|
|
await c.forward();
|
|
} catch (_) {
|
|
// klient zavřel dřív — pro test nepodstatné
|
|
}
|
|
}).onError((_) {});
|
|
final proxySock = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
|
|
proxyPort = proxySock.port;
|
|
await proxy.addServerSocket(proxySock);
|
|
|
|
// Cílový server: echo HTTP + echo WebSocket.
|
|
target = await HttpServer.bind(InternetAddress.loopbackIPv4, 0);
|
|
targetPort = target.port;
|
|
target.listen((req) async {
|
|
if (WebSocketTransformer.isUpgradeRequest(req)) {
|
|
final ws = await WebSocketTransformer.upgrade(req);
|
|
ws.listen((data) => ws.add(data)); // echo zpět
|
|
} else {
|
|
req.response
|
|
..statusCode = 200
|
|
..write('ozvěna:${req.uri.path}');
|
|
await req.response.close();
|
|
}
|
|
});
|
|
});
|
|
|
|
tearDown(() async {
|
|
await target.close(force: true);
|
|
await proxy.stop();
|
|
});
|
|
|
|
test('REST GET projde přes proxy k cíli', () async {
|
|
final t = TorTransport(socksHost: '127.0.0.1', socksPort: proxyPort);
|
|
final resp = await t
|
|
.httpClient()
|
|
.get(Uri.parse('http://127.0.0.1:$targetPort/ahoj'))
|
|
.timeout(const Duration(seconds: 10));
|
|
expect(resp.statusCode, 200);
|
|
expect(resp.body, 'ozvěna:/ahoj');
|
|
t.close();
|
|
});
|
|
|
|
test('WebSocket se otevře přes proxy a echuje', () async {
|
|
final t = TorTransport(socksHost: '127.0.0.1', socksPort: proxyPort);
|
|
final ch = t.connectWs(Uri.parse('ws://127.0.0.1:$targetPort/v1/ws'));
|
|
await ch.ready.timeout(const Duration(seconds: 10));
|
|
ch.sink.add('ping-přes-tor');
|
|
final echo = await ch.stream.first.timeout(const Duration(seconds: 10));
|
|
expect(echo, 'ping-přes-tor');
|
|
await ch.sink.close();
|
|
t.close();
|
|
});
|
|
|
|
test('DirectTransport míří na cíl přímo (kontrolní vzorek)', () async {
|
|
final t = DirectTransport();
|
|
final resp = await t
|
|
.httpClient()
|
|
.get(Uri.parse('http://127.0.0.1:$targetPort/primo'))
|
|
.timeout(const Duration(seconds: 10));
|
|
expect(resp.statusCode, 200);
|
|
expect(resp.body, 'ozvěna:/primo');
|
|
t.close();
|
|
});
|
|
});
|
|
}
|