mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 21:29:54 +01:00
Merge branch '1564-tor-rendezvous-crypto' into 'master'
Publish hidden service for connecting to pending contact Closes #1564 See merge request briar/briar!1125
This commit is contained in:
@@ -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 {
|
|||||||
|
|
||||||
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
@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;
|
|||||||
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;
|
|||||||
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
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
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 {
|
|||||||
}
|
}
|
||||||
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 {
|
|||||||
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 {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
@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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
@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
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package org.briarproject.bramble.plugin.tor;
|
||||||
|
|
||||||
|
interface TorRendezvousCrypto {
|
||||||
|
|
||||||
|
static final int SEED_BYTES = 32;
|
||||||
|
|
||||||
|
String getOnionAddress(byte[] seed);
|
||||||
|
|
||||||
|
String getPrivateKeyBlob(byte[] seed);
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
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,
|
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 {
|
|||||||
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 {
|
|||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user