diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementListener.java b/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementListener.java index 8c520b47e..9ace16f09 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementListener.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/keyagreement/KeyAgreementListener.java @@ -5,7 +5,7 @@ import org.briarproject.bramble.api.data.BdfList; import java.io.IOException; /** - * An class for managing a particular key agreement listener. + * Accepts key agreement connections over a given transport. */ public abstract class KeyAgreementListener { diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginManager.java index 32c3a8012..9c34b434d 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginManager.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/PluginManager.java @@ -36,4 +36,9 @@ public interface PluginManager { * Returns any duplex plugins that support key agreement. */ Collection getKeyAgreementPlugins(); + + /** + * Returns any duplex plugins that support rendezvous. + */ + Collection getRendezvousPlugins(); } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/DuplexPlugin.java b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/DuplexPlugin.java index 5633d77ee..4ce802ced 100644 --- a/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/DuplexPlugin.java +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/plugin/duplex/DuplexPlugin.java @@ -5,6 +5,8 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.plugin.Plugin; import org.briarproject.bramble.api.properties.TransportProperties; +import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; +import org.briarproject.bramble.api.rendezvous.RendezvousHandler; import javax.annotation.Nullable; @@ -40,4 +42,15 @@ public interface DuplexPlugin extends Plugin { @Nullable DuplexTransportConnection createKeyAgreementConnection( byte[] remoteCommitment, BdfList descriptor); + + /** + * Returns true if the plugin supports rendezvous connections. + */ + boolean supportsRendezvous(); + + /** + * Creates and returns a handler that uses the given key material to + * rendezvous with a pending contact. + */ + RendezvousHandler createRendezvousHandler(KeyMaterialSource k); } diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/KeyMaterialSource.java b/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/KeyMaterialSource.java new file mode 100644 index 000000000..87d1960da --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/KeyMaterialSource.java @@ -0,0 +1,15 @@ +package org.briarproject.bramble.api.rendezvous; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +/** + * A source of key material for use in making rendezvous connections. + */ +@NotNullByDefault +public interface KeyMaterialSource { + + /** + * Returns the requested amount of key material. + */ + byte[] getKeyMaterial(int length); +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousConstants.java b/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousConstants.java new file mode 100644 index 000000000..00ac61fe1 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousConstants.java @@ -0,0 +1,10 @@ +package org.briarproject.bramble.api.rendezvous; + +public interface RendezvousConstants { + + /** + * Label for deriving key material from the master key. + */ + String KEY_MATERIAL_LABEL = + "org.briarproject.bramble.rendezvous/KEY_MATERIAL"; +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousCrypto.java b/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousCrypto.java new file mode 100644 index 000000000..e6cacef09 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousCrypto.java @@ -0,0 +1,12 @@ +package org.briarproject.bramble.api.rendezvous; + +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.TransportId; + +@NotNullByDefault +public interface RendezvousCrypto { + + KeyMaterialSource createKeyMaterialSource(SecretKey masterKey, + TransportId t); +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousHandler.java b/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousHandler.java new file mode 100644 index 000000000..a48e2d619 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/rendezvous/RendezvousHandler.java @@ -0,0 +1,24 @@ +package org.briarproject.bramble.api.rendezvous; + +import org.briarproject.bramble.api.properties.TransportProperties; + +import java.io.IOException; + +/** + * An interface for making and accepting rendezvous connections with a pending + * contact over a given transport. + */ +public interface RendezvousHandler { + + /** + * Returns a set of transport properties for connecting to the pending + * contact. + */ + TransportProperties getRemoteTransportProperties(); + + /** + * Closes the handler and releases any resources held by it, such as + * network sockets. + */ + void close() throws IOException; +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/BrambleCoreModule.java b/bramble-core/src/main/java/org/briarproject/bramble/BrambleCoreModule.java index 44535a50a..ebf43a69d 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/BrambleCoreModule.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/BrambleCoreModule.java @@ -15,6 +15,7 @@ import org.briarproject.bramble.plugin.PluginModule; import org.briarproject.bramble.properties.PropertiesModule; import org.briarproject.bramble.record.RecordModule; import org.briarproject.bramble.reliability.ReliabilityModule; +import org.briarproject.bramble.rendezvous.RendezvousModule; import org.briarproject.bramble.reporting.ReportingModule; import org.briarproject.bramble.settings.SettingsModule; import org.briarproject.bramble.socks.SocksModule; @@ -42,6 +43,7 @@ import dagger.Module; PropertiesModule.class, RecordModule.class, ReliabilityModule.class, + RendezvousModule.class, ReportingModule.class, SettingsModule.class, SocksModule.class, diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java index 682b7fde2..cbaac0943 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/PluginManagerImpl.java @@ -169,6 +169,14 @@ class PluginManagerImpl implements PluginManager, Service { return supported; } + @Override + public Collection getRendezvousPlugins() { + List supported = new ArrayList<>(); + for (DuplexPlugin d : duplexPlugins) + if (d.supportsRendezvous()) supported.add(d); + return supported; + } + private class PluginStarter implements Runnable { private final Plugin plugin; diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java index 4cb3fecdc..04e54027b 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/bluetooth/BluetoothPlugin.java @@ -22,6 +22,8 @@ import org.briarproject.bramble.api.plugin.event.BluetoothEnabledEvent; import org.briarproject.bramble.api.plugin.event.DisableBluetoothEvent; import org.briarproject.bramble.api.plugin.event.EnableBluetoothEvent; import org.briarproject.bramble.api.properties.TransportProperties; +import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; +import org.briarproject.bramble.api.rendezvous.RendezvousHandler; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; @@ -390,6 +392,16 @@ abstract class BluetoothPlugin implements DuplexPlugin, EventListener { return macToString(mac); } + @Override + public boolean supportsRendezvous() { + return false; + } + + @Override + public RendezvousHandler createRendezvousHandler(KeyMaterialSource k) { + throw new UnsupportedOperationException(); + } + @Override public void eventOccurred(Event e) { if (e instanceof EnableBluetoothEvent) { diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java index aa35274d7..5bc579487 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tcp/TcpPlugin.java @@ -12,6 +12,8 @@ import org.briarproject.bramble.api.plugin.PluginCallback; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.properties.TransportProperties; +import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; +import org.briarproject.bramble.api.rendezvous.RendezvousHandler; import org.briarproject.bramble.util.IoUtils; import java.io.IOException; @@ -301,6 +303,16 @@ abstract class TcpPlugin implements DuplexPlugin { throw new UnsupportedOperationException(); } + @Override + public boolean supportsRendezvous() { + return false; + } + + @Override + public RendezvousHandler createRendezvousHandler(KeyMaterialSource k) { + throw new UnsupportedOperationException(); + } + Collection getLocalIpAddresses() { try { Enumeration ifaces = getNetworkInterfaces(); diff --git a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java index 38efad254..5cfcf5db4 100644 --- a/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java +++ b/bramble-core/src/main/java/org/briarproject/bramble/plugin/tor/TorPlugin.java @@ -25,6 +25,8 @@ import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.properties.TransportProperties; +import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; +import org.briarproject.bramble.api.rendezvous.RendezvousHandler; import org.briarproject.bramble.api.settings.Settings; import org.briarproject.bramble.api.settings.event.SettingsUpdatedEvent; import org.briarproject.bramble.api.system.Clock; @@ -43,9 +45,7 @@ import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Scanner; @@ -58,11 +58,15 @@ import java.util.zip.ZipInputStream; import javax.annotation.Nullable; import javax.net.SocketFactory; +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static java.util.Collections.singletonMap; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static net.freehaven.tor.control.TorControlCommands.HS_ADDRESS; import static net.freehaven.tor.control.TorControlCommands.HS_PRIVKEY; +import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.plugin.TorConstants.CONTROL_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.ID; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_MOBILE; @@ -74,6 +78,7 @@ 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.util.IoUtils.copyAndClose; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.PrivacyUtils.scrubOnion; import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty; @@ -251,11 +256,11 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { controlConnection.authenticate(read(cookieFile)); // Tell Tor to exit when the control connection is closed controlConnection.takeOwnership(); - controlConnection.resetConf(Collections.singletonList(OWNER)); + controlConnection.resetConf(singletonList(OWNER)); running = true; // Register to receive events from the Tor process controlConnection.setEventHandler(this); - controlConnection.setEvents(Arrays.asList(EVENTS)); + controlConnection.setEvents(asList(EVENTS)); // Check whether Tor has already bootstrapped String phase = controlConnection.getInfo("status/bootstrap-phase"); if (phase != null && phase.contains("PROGRESS=100")) { @@ -280,28 +285,31 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { InputStream in = null; OutputStream out = null; try { + // The done file may already exist from a previous installation + //noinspection ResultOfMethodCallIgnored doneFile.delete(); // Unzip the Tor binary to the filesystem in = getTorInputStream(); out = new FileOutputStream(torFile); - IoUtils.copyAndClose(in, out); + copyAndClose(in, out); // Make the Tor binary executable if (!torFile.setExecutable(true, true)) throw new IOException(); // Unzip the GeoIP database to the filesystem in = getGeoIpInputStream(); out = new FileOutputStream(geoIpFile); - IoUtils.copyAndClose(in, out); + copyAndClose(in, out); // Unzip the Obfs4 proxy to the filesystem in = getObfs4InputStream(); out = new FileOutputStream(obfs4File); - IoUtils.copyAndClose(in, out); + copyAndClose(in, out); // Make the Obfs4 proxy executable if (!obfs4File.setExecutable(true, true)) throw new IOException(); // Copy the config file to the filesystem in = getConfigInputStream(); out = new FileOutputStream(configFile); - IoUtils.copyAndClose(in, out); - doneFile.createNewFile(); + copyAndClose(in, out); + if (!doneFile.createNewFile()) + LOG.warning("Failed to create done file"); } catch (IOException e) { IoUtils.tryToClose(in, LOG, WARNING); IoUtils.tryToClose(out, LOG, WARNING); @@ -338,7 +346,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { } private InputStream getConfigInputStream() { - return getClass().getClassLoader().getResourceAsStream("torrc"); + ClassLoader cl = getClass().getClassLoader(); + return requireNonNull(cl.getResourceAsStream("torrc")); } private void listFiles(File f) { @@ -410,8 +419,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { if (!running) return; LOG.info("Creating hidden service"); String privKey = settings.get(HS_PRIVKEY); - Map portLines = - Collections.singletonMap(80, "127.0.0.1:" + port); + Map portLines = singletonMap(80, "127.0.0.1:" + port); Map response; try { // Use the control connection to set up the hidden service @@ -599,6 +607,16 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener { throw new UnsupportedOperationException(); } + @Override + public boolean supportsRendezvous() { + return false; + } + + @Override + public RendezvousHandler createRendezvousHandler(KeyMaterialSource k) { + throw new UnsupportedOperationException(); + } + @Override public void circuitStatus(String status, String id, String path) { if (status.equals("BUILT") && diff --git a/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/KeyMaterialSourceImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/KeyMaterialSourceImpl.java new file mode 100644 index 000000000..7a2ec3508 --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/KeyMaterialSourceImpl.java @@ -0,0 +1,33 @@ +package org.briarproject.bramble.rendezvous; + +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; +import org.spongycastle.crypto.engines.Salsa20Engine; +import org.spongycastle.crypto.params.KeyParameter; +import org.spongycastle.crypto.params.ParametersWithIV; + +import javax.annotation.concurrent.GuardedBy; +import javax.annotation.concurrent.ThreadSafe; + +@ThreadSafe +@NotNullByDefault +class KeyMaterialSourceImpl implements KeyMaterialSource { + + @GuardedBy("this") + private final Salsa20Engine cipher = new Salsa20Engine(); + + KeyMaterialSourceImpl(SecretKey sourceKey) { + // Initialise the stream cipher with an all-zero nonce + KeyParameter k = new KeyParameter(sourceKey.getBytes()); + cipher.init(true, new ParametersWithIV(k, new byte[8])); + } + + @Override + public synchronized byte[] getKeyMaterial(int length) { + byte[] in = new byte[length]; + byte[] out = new byte[length]; + cipher.processBytes(in, 0, length, out, 0); + return out; + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousCryptoImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousCryptoImpl.java new file mode 100644 index 000000000..01877ae48 --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousCryptoImpl.java @@ -0,0 +1,34 @@ +package org.briarproject.bramble.rendezvous; + +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.plugin.TransportId; +import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; +import org.briarproject.bramble.api.rendezvous.RendezvousCrypto; + +import javax.annotation.concurrent.Immutable; +import javax.inject.Inject; + +import static org.briarproject.bramble.api.rendezvous.RendezvousConstants.KEY_MATERIAL_LABEL; +import static org.briarproject.bramble.util.StringUtils.toUtf8; + +@Immutable +@NotNullByDefault +class RendezvousCryptoImpl implements RendezvousCrypto { + + private final CryptoComponent crypto; + + @Inject + RendezvousCryptoImpl(CryptoComponent crypto) { + this.crypto = crypto; + } + + @Override + public KeyMaterialSource createKeyMaterialSource(SecretKey masterKey, + TransportId t) { + SecretKey sourceKey = crypto.deriveKey(KEY_MATERIAL_LABEL, masterKey, + toUtf8(t.getString())); + return new KeyMaterialSourceImpl(sourceKey); + } +} diff --git a/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousModule.java b/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousModule.java new file mode 100644 index 000000000..f166303a5 --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/rendezvous/RendezvousModule.java @@ -0,0 +1,16 @@ +package org.briarproject.bramble.rendezvous; + +import org.briarproject.bramble.api.rendezvous.RendezvousCrypto; + +import dagger.Module; +import dagger.Provides; + +@Module +public class RendezvousModule { + + @Provides + RendezvousCrypto provideRendezvousCrypto( + RendezvousCryptoImpl rendezvousCrypto) { + return rendezvousCrypto; + } +} diff --git a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java index f6449ccc9..980db3af9 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/plugin/modem/ModemPlugin.java @@ -13,6 +13,8 @@ import org.briarproject.bramble.api.plugin.duplex.AbstractDuplexTransportConnect import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection; import org.briarproject.bramble.api.properties.TransportProperties; +import org.briarproject.bramble.api.rendezvous.KeyMaterialSource; +import org.briarproject.bramble.api.rendezvous.RendezvousHandler; import java.io.IOException; import java.io.InputStream; @@ -184,6 +186,16 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback { throw new UnsupportedOperationException(); } + @Override + public boolean supportsRendezvous() { + return false; + } + + @Override + public RendezvousHandler createRendezvousHandler(KeyMaterialSource k) { + throw new UnsupportedOperationException(); + } + @Override public void incomingCallConnected() { LOG.info("Incoming call connected"); diff --git a/bramble-java/src/main/java/org/briarproject/bramble/system/JavaResourceProvider.java b/bramble-java/src/main/java/org/briarproject/bramble/system/JavaResourceProvider.java index 144bff2de..faf5068b4 100644 --- a/bramble-java/src/main/java/org/briarproject/bramble/system/JavaResourceProvider.java +++ b/bramble-java/src/main/java/org/briarproject/bramble/system/JavaResourceProvider.java @@ -7,6 +7,8 @@ import java.io.InputStream; import javax.inject.Inject; +import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; + @NotNullByDefault class JavaResourceProvider implements ResourceProvider { @@ -16,7 +18,7 @@ class JavaResourceProvider implements ResourceProvider { @Override public InputStream getResourceInputStream(String name, String extension) { - return getClass().getClassLoader() - .getResourceAsStream(name + extension); + ClassLoader cl = getClass().getClassLoader(); + return requireNonNull(cl.getResourceAsStream(name + extension)); } }