Commit f31b85ac authored by Torsten Grote's avatar Torsten Grote

Merge branch '1564-tor-rendezvous-crypto' into 'master'

Publish hidden service for connecting to pending contact

Closes #1564

See merge request !1125
parents 65197065 60eefbf3
Pipeline #3506 passed with stage
in 8 minutes and 40 seconds
...@@ -41,11 +41,12 @@ class AndroidTorPlugin extends TorPlugin { ...@@ -41,11 +41,12 @@ class AndroidTorPlugin extends TorPlugin {
Clock clock, ResourceProvider resourceProvider, Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff, BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime) { int maxIdleTime) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager, clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, maxLatency, maxIdleTime, backoff, torRendezvousCrypto, callback, architecture, maxLatency, maxIdleTime,
appContext.getDir("tor", MODE_PRIVATE)); appContext.getDir("tor", MODE_PRIVATE));
this.appContext = appContext; this.appContext = appContext;
PowerManager pm = (PowerManager) PowerManager pm = (PowerManager)
......
...@@ -106,10 +106,12 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory { ...@@ -106,10 +106,12 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler, AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler,
appContext, networkManager, locationUtils, torSocketFactory, appContext, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager, clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, MAX_LATENCY, MAX_IDLE_TIME); backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }
......
...@@ -250,6 +250,7 @@ class PluginManagerImpl implements PluginManager, Service { ...@@ -250,6 +250,7 @@ class PluginManagerImpl implements PluginManager, Service {
private class Callback implements PluginCallback { private class Callback implements PluginCallback {
private final TransportId id; private final TransportId id;
private final AtomicBoolean enabled = new AtomicBoolean(false);
private Callback(TransportId id) { private Callback(TransportId id) {
this.id = id; this.id = id;
...@@ -295,12 +296,14 @@ class PluginManagerImpl implements PluginManager, Service { ...@@ -295,12 +296,14 @@ class PluginManagerImpl implements PluginManager, Service {
@Override @Override
public void transportEnabled() { public void transportEnabled() {
eventBus.broadcast(new TransportEnabledEvent(id)); if (!enabled.getAndSet(true))
eventBus.broadcast(new TransportEnabledEvent(id));
} }
@Override @Override
public void transportDisabled() { public void transportDisabled() {
eventBus.broadcast(new TransportDisabledEvent(id)); if (enabled.getAndSet(false))
eventBus.broadcast(new TransportDisabledEvent(id));
} }
@Override @Override
......
...@@ -32,7 +32,6 @@ import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; ...@@ -32,7 +32,6 @@ import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.util.IoUtils;
import java.io.EOFException; import java.io.EOFException;
import java.io.File; import java.io.File;
...@@ -55,7 +54,6 @@ import java.util.logging.Logger; ...@@ -55,7 +54,6 @@ import java.util.logging.Logger;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.zip.ZipInputStream; import java.util.zip.ZipInputStream;
import javax.annotation.Nullable;
import javax.net.SocketFactory; import javax.net.SocketFactory;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
...@@ -78,7 +76,9 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHE ...@@ -78,7 +76,9 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHE
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
import static org.briarproject.bramble.plugin.tor.TorRendezvousCrypto.SEED_BYTES;
import static org.briarproject.bramble.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.IoUtils.copyAndClose;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion; import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
...@@ -105,6 +105,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -105,6 +105,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final Clock clock; private final Clock clock;
private final BatteryManager batteryManager; private final BatteryManager batteryManager;
private final Backoff backoff; private final Backoff backoff;
private final TorRendezvousCrypto torRendezvousCrypto;
private final PluginCallback callback; private final PluginCallback callback;
private final String architecture; private final String architecture;
private final CircumventionProvider circumventionProvider; private final CircumventionProvider circumventionProvider;
...@@ -131,6 +132,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -131,6 +132,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
Clock clock, ResourceProvider resourceProvider, Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff, BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, File torDirectory) { int maxIdleTime, File torDirectory) {
this.ioExecutor = ioExecutor; this.ioExecutor = ioExecutor;
...@@ -142,6 +144,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -142,6 +144,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
this.circumventionProvider = circumventionProvider; this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager; this.batteryManager = batteryManager;
this.backoff = backoff; this.backoff = backoff;
this.torRendezvousCrypto = torRendezvousCrypto;
this.callback = callback; this.callback = callback;
this.architecture = architecture; this.architecture = architecture;
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
...@@ -311,8 +314,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -311,8 +314,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!doneFile.createNewFile()) if (!doneFile.createNewFile())
LOG.warning("Failed to create done file"); LOG.warning("Failed to create done file");
} catch (IOException e) { } catch (IOException e) {
IoUtils.tryToClose(in, LOG, WARNING); tryToClose(in, LOG, WARNING);
IoUtils.tryToClose(out, LOG, WARNING); tryToClose(out, LOG, WARNING);
throw new PluginException(e); throw new PluginException(e);
} }
} }
...@@ -371,7 +374,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -371,7 +374,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
return b; return b;
} finally { } finally {
IoUtils.tryToClose(in, LOG, WARNING); tryToClose(in, LOG, WARNING);
} }
} }
...@@ -389,11 +392,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -389,11 +392,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
ss.bind(new InetSocketAddress("127.0.0.1", port)); ss.bind(new InetSocketAddress("127.0.0.1", port));
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
tryToClose(ss); tryToClose(ss, LOG, WARNING);
return; return;
} }
if (!running) { if (!running) {
tryToClose(ss); tryToClose(ss, LOG, WARNING);
return; return;
} }
socket = ss; socket = ss;
...@@ -410,11 +413,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -410,11 +413,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}); });
} }
private void tryToClose(@Nullable ServerSocket ss) {
IoUtils.tryToClose(ss, LOG, WARNING);
callback.transportDisabled();
}
private void publishHiddenService(String port) { private void publishHiddenService(String port) {
if (!running) return; if (!running) return;
LOG.info("Creating hidden service"); LOG.info("Creating hidden service");
...@@ -499,7 +497,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -499,7 +497,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public void stop() { public void stop() {
running = false; running = false;
tryToClose(socket); tryToClose(socket, LOG, WARNING);
callback.transportDisabled();
if (controlSocket != null && controlConnection != null) { if (controlSocket != null && controlConnection != null) {
try { try {
LOG.info("Stopping Tor"); LOG.info("Stopping Tor");
...@@ -586,7 +585,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -586,7 +585,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.info("Could not connect to " + scrubOnion(bestOnion) LOG.info("Could not connect to " + scrubOnion(bestOnion)
+ ": " + e.toString()); + ": " + e.toString());
} }
IoUtils.tryToClose(s, LOG, WARNING); tryToClose(s, LOG, WARNING);
return null; return null;
} }
} }
...@@ -609,13 +608,58 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { ...@@ -609,13 +608,58 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public boolean supportsRendezvous() { public boolean supportsRendezvous() {
return false; return true;
} }
@Override @Override
public RendezvousEndpoint createRendezvousEndpoint(KeyMaterialSource k, public RendezvousEndpoint createRendezvousEndpoint(KeyMaterialSource k,
boolean alice, ConnectionHandler incoming) { boolean alice, ConnectionHandler incoming) {
throw new UnsupportedOperationException(); byte[] aliceSeed = k.getKeyMaterial(SEED_BYTES);
byte[] bobSeed = k.getKeyMaterial(SEED_BYTES);
byte[] localSeed = alice ? aliceSeed : bobSeed;
byte[] remoteSeed = alice ? bobSeed : aliceSeed;
String blob = torRendezvousCrypto.getPrivateKeyBlob(localSeed);
String localOnion = torRendezvousCrypto.getOnionAddress(localSeed);
String remoteOnion = torRendezvousCrypto.getOnionAddress(remoteSeed);
TransportProperties remoteProperties = new TransportProperties();
remoteProperties.put(PROP_ONION_V3, remoteOnion);
try {
ServerSocket ss = new ServerSocket();
ss.bind(new InetSocketAddress("127.0.0.1", 0));
int port = ss.getLocalPort();
ioExecutor.execute(() -> {
try {
//noinspection InfiniteLoopStatement
while (true) {
Socket s = ss.accept();
incoming.handleConnection(
new TorTransportConnection(this, s));
}
} catch (IOException e) {
// This is expected when the socket is closed
if (LOG.isLoggable(INFO)) LOG.info(e.toString());
}
});
Map<Integer, String> portLines =
singletonMap(80, "127.0.0.1:" + port);
controlConnection.addOnion(blob, portLines);
return new RendezvousEndpoint() {
@Override
public TransportProperties getRemoteTransportProperties() {
return remoteProperties;
}
@Override
public void close() throws IOException {
controlConnection.delOnion(localOnion);
tryToClose(ss, LOG, WARNING);
}
};
} catch (IOException e) {
logException(LOG, WARNING, e);
return null;
}
} }
@Override @Override
......
package org.briarproject.bramble.plugin.tor;
interface TorRendezvousCrypto {
static final int SEED_BYTES = 32;
String getOnionAddress(byte[] seed);
String getPrivateKeyBlob(byte[] seed);
}
package org.briarproject.bramble.plugin.tor;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import org.briarproject.bramble.util.Base32;
import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.digests.SHA3Digest;
import org.spongycastle.util.encoders.Base64;
import java.nio.charset.Charset;
import static java.lang.System.arraycopy;
public class TorRendezvousCryptoImpl implements TorRendezvousCrypto {
private static final EdDSANamedCurveSpec CURVE_SPEC =
EdDSANamedCurveTable.getByName("Ed25519");
private static final byte HS_PROTOCOL_VERSION = 3;
private static final int CHECKSUM_BYTES = 2;
@Override
public String getOnionAddress(byte[] seed) {
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
byte[] publicKey = spec.getA().toByteArray();
Digest digest = new SHA3Digest(256);
byte[] label = ".onion checksum".getBytes(Charset.forName("US-ASCII"));
digest.update(label, 0, label.length);
digest.update(publicKey, 0, publicKey.length);
digest.update(HS_PROTOCOL_VERSION);
byte[] checksum = new byte[digest.getDigestSize()];
digest.doFinal(checksum, 0);
byte[] address = new byte[publicKey.length + CHECKSUM_BYTES + 1];
arraycopy(publicKey, 0, address, 0, publicKey.length);
arraycopy(checksum, 0, address, publicKey.length, CHECKSUM_BYTES);
address[address.length - 1] = HS_PROTOCOL_VERSION;
return Base32.encode(address).toLowerCase();
}
@Override
public String getPrivateKeyBlob(byte[] seed) {
EdDSAPrivateKeySpec spec = new EdDSAPrivateKeySpec(seed, CURVE_SPEC);
byte[] hash = spec.getH();
byte[] base64 = Base64.encode(hash);
return "ED25519-V3:" + new String(base64, Charset.forName("US-ASCII"));
}
}
...@@ -25,12 +25,13 @@ abstract class JavaTorPlugin extends TorPlugin { ...@@ -25,12 +25,13 @@ abstract class JavaTorPlugin extends TorPlugin {
Clock clock, ResourceProvider resourceProvider, Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff, BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, File torDirectory) { int maxIdleTime, File torDirectory) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager, clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, maxLatency, maxIdleTime, backoff, torRendezvousCrypto, callback, architecture,
torDirectory); maxLatency, maxIdleTime, torDirectory);
} }
@Override @Override
......
...@@ -25,12 +25,13 @@ class UnixTorPlugin extends JavaTorPlugin { ...@@ -25,12 +25,13 @@ class UnixTorPlugin extends JavaTorPlugin {
Clock clock, ResourceProvider resourceProvider, Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff, BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency, PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, File torDirectory) { int maxIdleTime, File torDirectory) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory, super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager, clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, maxLatency, maxIdleTime, backoff, torRendezvousCrypto, callback, architecture,
torDirectory); maxLatency, maxIdleTime, torDirectory);
} }
@Override @Override
......
...@@ -96,10 +96,12 @@ public class UnixTorPluginFactory implements DuplexPluginFactory { ...@@ -96,10 +96,12 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL, Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE); MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager, UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager,
locationUtils, torSocketFactory, clock, resourceProvider, locationUtils, torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, backoff, callback, circumventionProvider, batteryManager, backoff,
architecture, MAX_LATENCY, MAX_IDLE_TIME, torDirectory); torRendezvousCrypto, callback, architecture, MAX_LATENCY,
MAX_IDLE_TIME, torDirectory);
eventBus.addListener(plugin); eventBus.addListener(plugin);
return plugin; return plugin;
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment