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 {
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, maxLatency, maxIdleTime,
backoff, torRendezvousCrypto, callback, architecture, maxLatency, maxIdleTime,
appContext.getDir("tor", MODE_PRIVATE));
this.appContext = appContext;
PowerManager pm = (PowerManager)
......
......@@ -106,10 +106,12 @@ public class AndroidTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
AndroidTorPlugin plugin = new AndroidTorPlugin(ioExecutor, scheduler,
appContext, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, MAX_LATENCY, MAX_IDLE_TIME);
backoff, torRendezvousCrypto, callback, architecture,
MAX_LATENCY, MAX_IDLE_TIME);
eventBus.addListener(plugin);
return plugin;
}
......
......@@ -250,6 +250,7 @@ class PluginManagerImpl implements PluginManager, Service {
private class Callback implements PluginCallback {
private final TransportId id;
private final AtomicBoolean enabled = new AtomicBoolean(false);
private Callback(TransportId id) {
this.id = id;
......@@ -295,11 +296,13 @@ class PluginManagerImpl implements PluginManager, Service {
@Override
public void transportEnabled() {
if (!enabled.getAndSet(true))
eventBus.broadcast(new TransportEnabledEvent(id));
}
@Override
public void transportDisabled() {
if (enabled.getAndSet(false))
eventBus.broadcast(new TransportDisabledEvent(id));
}
......
......@@ -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.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.util.IoUtils;
import java.io.EOFException;
import java.io.File;
......@@ -55,7 +54,6 @@ import java.util.logging.Logger;
import java.util.regex.Pattern;
import java.util.zip.ZipInputStream;
import javax.annotation.Nullable;
import javax.net.SocketFactory;
import static java.util.Arrays.asList;
......@@ -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.PROP_ONION_V2;
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.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion;
import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
......@@ -105,6 +105,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
private final Clock clock;
private final BatteryManager batteryManager;
private final Backoff backoff;
private final TorRendezvousCrypto torRendezvousCrypto;
private final PluginCallback callback;
private final String architecture;
private final CircumventionProvider circumventionProvider;
......@@ -131,6 +132,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, File torDirectory) {
this.ioExecutor = ioExecutor;
......@@ -142,6 +144,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
this.circumventionProvider = circumventionProvider;
this.batteryManager = batteryManager;
this.backoff = backoff;
this.torRendezvousCrypto = torRendezvousCrypto;
this.callback = callback;
this.architecture = architecture;
this.maxLatency = maxLatency;
......@@ -311,8 +314,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!doneFile.createNewFile())
LOG.warning("Failed to create done file");
} catch (IOException e) {
IoUtils.tryToClose(in, LOG, WARNING);
IoUtils.tryToClose(out, LOG, WARNING);
tryToClose(in, LOG, WARNING);
tryToClose(out, LOG, WARNING);
throw new PluginException(e);
}
}
......@@ -371,7 +374,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
return b;
} finally {
IoUtils.tryToClose(in, LOG, WARNING);
tryToClose(in, LOG, WARNING);
}
}
......@@ -389,11 +392,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
ss.bind(new InetSocketAddress("127.0.0.1", port));
} catch (IOException e) {
logException(LOG, WARNING, e);
tryToClose(ss);
tryToClose(ss, LOG, WARNING);
return;
}
if (!running) {
tryToClose(ss);
tryToClose(ss, LOG, WARNING);
return;
}
socket = ss;
......@@ -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) {
if (!running) return;
LOG.info("Creating hidden service");
......@@ -499,7 +497,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public void stop() {
running = false;
tryToClose(socket);
tryToClose(socket, LOG, WARNING);
callback.transportDisabled();
if (controlSocket != null && controlConnection != null) {
try {
LOG.info("Stopping Tor");
......@@ -586,7 +585,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.info("Could not connect to " + scrubOnion(bestOnion)
+ ": " + e.toString());
}
IoUtils.tryToClose(s, LOG, WARNING);
tryToClose(s, LOG, WARNING);
return null;
}
}
......@@ -609,13 +608,58 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override
public boolean supportsRendezvous() {
return false;
return true;
}
@Override
public RendezvousEndpoint createRendezvousEndpoint(KeyMaterialSource k,
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
......
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 {
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, File torDirectory) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, maxLatency, maxIdleTime,
torDirectory);
backoff, torRendezvousCrypto, callback, architecture,
maxLatency, maxIdleTime, torDirectory);
}
@Override
......
......@@ -25,12 +25,13 @@ class UnixTorPlugin extends JavaTorPlugin {
Clock clock, ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider,
BatteryManager batteryManager, Backoff backoff,
TorRendezvousCrypto torRendezvousCrypto,
PluginCallback callback, String architecture, int maxLatency,
int maxIdleTime, File torDirectory) {
super(ioExecutor, networkManager, locationUtils, torSocketFactory,
clock, resourceProvider, circumventionProvider, batteryManager,
backoff, callback, architecture, maxLatency, maxIdleTime,
torDirectory);
backoff, torRendezvousCrypto, callback, architecture,
maxLatency, maxIdleTime, torDirectory);
}
@Override
......
......@@ -96,10 +96,12 @@ public class UnixTorPluginFactory implements DuplexPluginFactory {
Backoff backoff = backoffFactory.createBackoff(MIN_POLLING_INTERVAL,
MAX_POLLING_INTERVAL, BACKOFF_BASE);
TorRendezvousCrypto torRendezvousCrypto = new TorRendezvousCryptoImpl();
UnixTorPlugin plugin = new UnixTorPlugin(ioExecutor, networkManager,
locationUtils, torSocketFactory, clock, resourceProvider,
circumventionProvider, batteryManager, backoff, callback,
architecture, MAX_LATENCY, MAX_IDLE_TIME, torDirectory);
circumventionProvider, batteryManager, backoff,
torRendezvousCrypto, callback, architecture, MAX_LATENCY,
MAX_IDLE_TIME, torDirectory);
eventBus.addListener(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