diff --git a/briar-core/src/net/sf/briar/plugins/PluginsModule.java b/briar-core/src/net/sf/briar/plugins/PluginsModule.java index d4c192d763403c7ae92827f7f37b2c4e548bd131..fe31c6e7db61434efa77b0d4f7cf61e421bae37b 100644 --- a/briar-core/src/net/sf/briar/plugins/PluginsModule.java +++ b/briar-core/src/net/sf/briar/plugins/PluginsModule.java @@ -6,6 +6,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import net.sf.briar.api.android.AndroidExecutor; +import net.sf.briar.api.crypto.CryptoComponent; import net.sf.briar.api.lifecycle.ShutdownManager; import net.sf.briar.api.plugins.PluginExecutor; import net.sf.briar.api.plugins.PluginManager; @@ -63,14 +64,15 @@ public class PluginsModule extends AbstractModule { @PluginExecutor ExecutorService pluginExecutor, AndroidExecutor androidExecutor, Context appContext, ReliabilityLayerFactory reliabilityFactory, - ShutdownManager shutdownManager) { + ShutdownManager shutdownManager, CryptoComponent crypto) { final Collection<DuplexPluginFactory> factories = new ArrayList<DuplexPluginFactory>(); if(OsUtils.isAndroid()) { factories.add(new DroidtoothPluginFactory(pluginExecutor, - androidExecutor, appContext)); + androidExecutor, appContext, crypto.getSecureRandom())); } else { - factories.add(new BluetoothPluginFactory(pluginExecutor)); + factories.add(new BluetoothPluginFactory(pluginExecutor, + crypto.getSecureRandom())); factories.add(new ModemPluginFactory(pluginExecutor, reliabilityFactory)); } diff --git a/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java b/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java index 639afb122b23b1ea96a5ca3756389c8341039f9e..d8fa83b7af7ba5b5b697d7d75229dfb66beaa466 100644 --- a/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java +++ b/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPlugin.java @@ -6,6 +6,7 @@ import static java.util.logging.Level.WARNING; import static javax.bluetooth.DiscoveryAgent.GIAC; import java.io.IOException; +import java.security.SecureRandom; import java.util.Collection; import java.util.Map; import java.util.Map.Entry; @@ -14,6 +15,7 @@ import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.Semaphore; import java.util.logging.Logger; import javax.bluetooth.BluetoothStateException; @@ -46,12 +48,14 @@ class BluetoothPlugin implements DuplexPlugin { private static final Logger LOG = Logger.getLogger(BluetoothPlugin.class.getName()); + private static final int UUID_BYTES = 16; private final Executor pluginExecutor; private final Clock clock; + private final SecureRandom secureRandom; private final DuplexPluginCallback callback; private final long maxLatency, pollingInterval; - private final Object discoveryLock = new Object(); + private final Semaphore discoverySemaphore = new Semaphore(1); private final ScheduledExecutorService scheduler; private volatile boolean running = false; @@ -61,10 +65,11 @@ class BluetoothPlugin implements DuplexPlugin { private volatile LocalDevice localDevice = null; BluetoothPlugin(@PluginExecutor Executor pluginExecutor, Clock clock, - DuplexPluginCallback callback, long maxLatency, - long pollingInterval) { + SecureRandom secureRandom, DuplexPluginCallback callback, + long maxLatency, long pollingInterval) { this.pluginExecutor = pluginExecutor; this.clock = clock; + this.secureRandom = secureRandom; this.callback = callback; this.maxLatency = maxLatency; this.pollingInterval = pollingInterval; @@ -130,9 +135,17 @@ class BluetoothPlugin implements DuplexPlugin { return "btspp://" + address + ":" + uuid + ";name=RFCOMM"; } - // FIXME: Get the UUID from the local transport properties private String getUuid() { - return UUID.nameUUIDFromBytes(new byte[0]).toString(); + String uuid = callback.getLocalProperties().get("uuid"); + if(uuid == null) { + byte[] random = new byte[UUID_BYTES]; + secureRandom.nextBytes(random); + uuid = UUID.nameUUIDFromBytes(random).toString(); + TransportProperties p = new TransportProperties(); + p.put("uuid", uuid); + callback.mergeLocalProperties(p); + } + return uuid; } private void tryToClose(StreamConnectionNotifier scn) { @@ -229,29 +242,33 @@ class BluetoothPlugin implements DuplexPlugin { long timeout) { if(!running) return null; // Use the same pseudo-random UUID as the contact - String uuid = generateUuid(r.nextBytes(16)); + byte[] b = r.nextBytes(UUID_BYTES); + String uuid = UUID.nameUUIDFromBytes(b).toString(); // Discover nearby devices and connect to any with the right UUID DiscoveryAgent discoveryAgent = localDevice.getDiscoveryAgent(); long end = clock.currentTimeMillis() + timeout; String url = null; while(url == null && clock.currentTimeMillis() < end) { - InvitationListener listener = - new InvitationListener(discoveryAgent, uuid); - // FIXME: Avoid making alien calls with a lock held - synchronized(discoveryLock) { - try { - discoveryAgent.startInquiry(GIAC, listener); - url = listener.waitForUrl(); - } catch(BluetoothStateException e) { - if(LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - return null; - } catch(InterruptedException e) { - if(LOG.isLoggable(INFO)) - LOG.info("Interrupted while waiting for URL"); - Thread.currentThread().interrupt(); - return null; - } + if(!discoverySemaphore.tryAcquire()) { + if(LOG.isLoggable(INFO)) + LOG.info("Another device discovery is in progress"); + return null; + } + try { + InvitationListener listener = + new InvitationListener(discoveryAgent, uuid); + discoveryAgent.startInquiry(GIAC, listener); + url = listener.waitForUrl(); + } catch(BluetoothStateException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + return null; + } catch(InterruptedException e) { + if(LOG.isLoggable(INFO)) + LOG.info("Interrupted while waiting for URL"); + Thread.currentThread().interrupt(); + return null; + } finally { + discoverySemaphore.release(); } if(!running) return null; } @@ -259,16 +276,12 @@ class BluetoothPlugin implements DuplexPlugin { return connect(url); } - private String generateUuid(byte[] b) { - UUID uuid = UUID.nameUUIDFromBytes(b); - return uuid.toString().replaceAll("-", ""); - } - public DuplexTransportConnection acceptInvitation(PseudoRandom r, long timeout) { if(!running) return null; // Use the same pseudo-random UUID as the contact - String uuid = generateUuid(r.nextBytes(16)); + byte[] b = r.nextBytes(UUID_BYTES); + String uuid = UUID.nameUUIDFromBytes(b).toString(); String url = makeUrl("localhost", uuid); // Make the device discoverable if possible makeDeviceDiscoverable(); diff --git a/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPluginFactory.java b/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPluginFactory.java index a3e4beac89753932b1cd92d93cc87f022aa69336..b3e225cdc6c8ec6332e48c70cd4a64fdd995f134 100644 --- a/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPluginFactory.java +++ b/briar-core/src/net/sf/briar/plugins/bluetooth/BluetoothPluginFactory.java @@ -1,5 +1,6 @@ package net.sf.briar.plugins.bluetooth; +import java.security.SecureRandom; import java.util.concurrent.Executor; import net.sf.briar.api.clock.Clock; @@ -16,10 +17,13 @@ public class BluetoothPluginFactory implements DuplexPluginFactory { private static final long POLLING_INTERVAL = 3 * 60 * 1000; // 3 minutes private final Executor pluginExecutor; + private final SecureRandom secureRandom; private final Clock clock; - public BluetoothPluginFactory(@PluginExecutor Executor pluginExecutor) { + public BluetoothPluginFactory(@PluginExecutor Executor pluginExecutor, + SecureRandom secureRandom) { this.pluginExecutor = pluginExecutor; + this.secureRandom = secureRandom; clock = new SystemClock(); } @@ -28,7 +32,7 @@ public class BluetoothPluginFactory implements DuplexPluginFactory { } public DuplexPlugin createPlugin(DuplexPluginCallback callback) { - return new BluetoothPlugin(pluginExecutor, clock, callback, - MAX_LATENCY, POLLING_INTERVAL); + return new BluetoothPlugin(pluginExecutor, clock, secureRandom, + callback, MAX_LATENCY, POLLING_INTERVAL); } } diff --git a/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java b/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java index 34d1c09ac6d0443aa0a917dea3d6083c639d13a8..099d98ec1334a98b0f85577e2ca0e51fbe5811c5 100644 --- a/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java +++ b/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPlugin.java @@ -11,6 +11,7 @@ import static java.util.logging.Level.WARNING; import java.io.IOException; import java.net.SocketTimeoutException; +import java.security.SecureRandom; import java.util.ArrayList; import java.util.Collection; import java.util.Map; @@ -52,6 +53,7 @@ class DroidtoothPlugin implements DuplexPlugin { private static final Logger LOG = Logger.getLogger(DroidtoothPlugin.class.getName()); + private static final int UUID_BYTES = 16; private static final String FOUND = "android.bluetooth.device.action.FOUND"; private static final String DISCOVERY_FINISHED = "android.bluetooth.adapter.action.DISCOVERY_FINISHED"; @@ -59,6 +61,7 @@ class DroidtoothPlugin implements DuplexPlugin { private final Executor pluginExecutor; private final AndroidExecutor androidExecutor; private final Context appContext; + private final SecureRandom secureRandom; private final DuplexPluginCallback callback; private final long maxLatency, pollingInterval; @@ -69,11 +72,12 @@ class DroidtoothPlugin implements DuplexPlugin { DroidtoothPlugin(@PluginExecutor Executor pluginExecutor, AndroidExecutor androidExecutor, Context appContext, - DuplexPluginCallback callback, long maxLatency, - long pollingInterval) { + SecureRandom secureRandom, DuplexPluginCallback callback, + long maxLatency, long pollingInterval) { this.pluginExecutor = pluginExecutor; this.androidExecutor = androidExecutor; this.appContext = appContext; + this.secureRandom = secureRandom; this.callback = callback; this.maxLatency = maxLatency; this.pollingInterval = pollingInterval; @@ -129,11 +133,12 @@ class DroidtoothPlugin implements DuplexPlugin { p.put("address", adapter.getAddress()); callback.mergeLocalProperties(p); // Bind a server socket to accept connections from contacts - BluetoothServerSocket ss; + BluetoothServerSocket ss = null; try { ss = InsecureBluetooth.listen(adapter, "RFCOMM", getUuid()); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + tryToClose(ss); return; } if(!running) { @@ -161,14 +166,22 @@ class DroidtoothPlugin implements DuplexPlugin { } } - // FIXME: Get the UUID from the local transport properties private UUID getUuid() { - return UUID.nameUUIDFromBytes(new byte[0]); + String uuid = callback.getLocalProperties().get("uuid"); + if(uuid == null) { + byte[] random = new byte[UUID_BYTES]; + secureRandom.nextBytes(random); + uuid = UUID.nameUUIDFromBytes(random).toString(); + TransportProperties p = new TransportProperties(); + p.put("uuid", uuid); + callback.mergeLocalProperties(p); + } + return UUID.fromString(uuid); } private void tryToClose(BluetoothServerSocket ss) { try { - ss.close(); + if(ss != null) ss.close(); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } @@ -244,16 +257,28 @@ class DroidtoothPlugin implements DuplexPlugin { } // Try to connect BluetoothDevice d = adapter.getRemoteDevice(address); + BluetoothSocket s = null; try { - BluetoothSocket s = InsecureBluetooth.createSocket(d, u); + s = InsecureBluetooth.createSocket(d, u); + if(LOG.isLoggable(INFO)) LOG.info("Connecting to " + address); s.connect(); + if(LOG.isLoggable(INFO)) LOG.info("Connected to " + address); return new DroidtoothTransportConnection(s, maxLatency); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + tryToClose(s); return null; } } + private void tryToClose(BluetoothSocket s) { + try { + if(s != null) s.close(); + } catch(IOException e) { + if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + } + } + public DuplexTransportConnection createConnection(ContactId c) { if(!running) return null; TransportProperties p = callback.getRemoteProperties().get(c); @@ -273,7 +298,9 @@ class DroidtoothPlugin implements DuplexPlugin { long timeout) { if(!running) return null; // Use the same pseudo-random UUID as the contact - String uuid = UUID.nameUUIDFromBytes(r.nextBytes(16)).toString(); + byte[] b = r.nextBytes(UUID_BYTES); + String uuid = UUID.nameUUIDFromBytes(b).toString(); + if(LOG.isLoggable(INFO)) LOG.info("Sending invitation, UUID " + uuid); // Register to receive Bluetooth discovery intents IntentFilter filter = new IntentFilter(); filter.addAction(FOUND); @@ -296,18 +323,25 @@ class DroidtoothPlugin implements DuplexPlugin { long timeout) { if(!running) return null; // Use the same pseudo-random UUID as the contact - UUID uuid = UUID.nameUUIDFromBytes(r.nextBytes(16)); + byte[] b = r.nextBytes(UUID_BYTES); + UUID uuid = UUID.nameUUIDFromBytes(b); + if(LOG.isLoggable(INFO)) LOG.info("Accepting invitation, UUID " + uuid); // Bind a new server socket to accept the invitation connection - final BluetoothServerSocket ss; + BluetoothServerSocket ss = null; try { ss = InsecureBluetooth.listen(adapter, "RFCOMM", uuid); } catch(IOException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); + tryToClose(ss); return null; } // Return the first connection received by the socket, if any try { BluetoothSocket s = ss.accept((int) timeout); + if(LOG.isLoggable(INFO)) { + String address = s.getRemoteDevice().getAddress(); + LOG.info("Incoming connection from " + address); + } return new DroidtoothTransportConnection(s, maxLatency); } catch(SocketTimeoutException e) { if(LOG.isLoggable(INFO)) LOG.info("Invitation timed out"); @@ -368,7 +402,8 @@ class DroidtoothPlugin implements DuplexPlugin { connectToDiscoveredDevices(); } else if(action.equals(FOUND)) { BluetoothDevice d = intent.getParcelableExtra(EXTRA_DEVICE); - addresses.add(d.getAddress()); + String address = d.getAddress(); + addresses.add(address); } } diff --git a/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPluginFactory.java b/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPluginFactory.java index 54f8a9c38c69f9502b1209c9b30d17a9fb6e4ae2..9c0cc7cf4c3c1bf81ee329216c69f60b5d46b9e6 100644 --- a/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPluginFactory.java +++ b/briar-core/src/net/sf/briar/plugins/droidtooth/DroidtoothPluginFactory.java @@ -1,5 +1,6 @@ package net.sf.briar.plugins.droidtooth; +import java.security.SecureRandom; import java.util.concurrent.Executor; import net.sf.briar.api.android.AndroidExecutor; @@ -18,12 +19,15 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory { private final Executor pluginExecutor; private final AndroidExecutor androidExecutor; private final Context appContext; + private final SecureRandom secureRandom; public DroidtoothPluginFactory(@PluginExecutor Executor pluginExecutor, - AndroidExecutor androidExecutor, Context appContext) { + AndroidExecutor androidExecutor, Context appContext, + SecureRandom secureRandom) { this.pluginExecutor = pluginExecutor; this.androidExecutor = androidExecutor; this.appContext = appContext; + this.secureRandom = secureRandom; } public TransportId getId() { @@ -32,6 +36,6 @@ public class DroidtoothPluginFactory implements DuplexPluginFactory { public DuplexPlugin createPlugin(DuplexPluginCallback callback) { return new DroidtoothPlugin(pluginExecutor, androidExecutor, appContext, - callback, MAX_LATENCY, POLLING_INTERVAL); + secureRandom, callback, MAX_LATENCY, POLLING_INTERVAL); } } diff --git a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java index 0bf02af7549f6070e9d8079b4071213eef5c4333..d137c2481f20ee7fc26ef8cd1a666ff19623d257 100644 --- a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java +++ b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java @@ -1,5 +1,6 @@ package net.sf.briar.plugins.bluetooth; +import java.security.SecureRandom; import java.util.Collections; import java.util.Map; import java.util.concurrent.Executor; @@ -20,14 +21,14 @@ public class BluetoothClientTest extends DuplexClientTest { // Store the server's Bluetooth address and UUID TransportProperties p = new TransportProperties(); p.put("address", serverAddress); - p.put("uuid", BluetoothTest.getUuid()); + p.put("uuid", BluetoothTest.EMPTY_UUID); Map<ContactId, TransportProperties> remote = Collections.singletonMap(contactId, p); // Create the plugin callback = new ClientCallback(new TransportConfig(), new TransportProperties(), remote); - plugin = new BluetoothPlugin(executor, new SystemClock(), callback, 0, - 0); + plugin = new BluetoothPlugin(executor, new SystemClock(), + new SecureRandom(), callback, 0, 0); } public static void main(String[] args) throws Exception { diff --git a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java index 2adf38df66c6e227a17276a041a737de391738ae..d60d2dda904f22e79c17b8ffd2f32fddb13295db 100644 --- a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java +++ b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java @@ -1,5 +1,6 @@ package net.sf.briar.plugins.bluetooth; +import java.security.SecureRandom; import java.util.Collections; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -17,12 +18,12 @@ public class BluetoothServerTest extends DuplexServerTest { private BluetoothServerTest(Executor executor) { // Store the UUID TransportProperties local = new TransportProperties(); - local.put("uuid", BluetoothTest.getUuid()); + local.put("uuid", BluetoothTest.EMPTY_UUID); // Create the plugin callback = new ServerCallback(new TransportConfig(), local, Collections.singletonMap(contactId, new TransportProperties())); - plugin = new BluetoothPlugin(executor, new SystemClock(), callback, 0, - 0); + plugin = new BluetoothPlugin(executor, new SystemClock(), + new SecureRandom(), callback, 0, 0); } public static void main(String[] args) throws Exception { diff --git a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothTest.java b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothTest.java index 17676bb08b254671e72d8fcb9d94d47db3d4ff95..f987dd39e46c5923fae3a4ea95b50b64a789cbf2 100644 --- a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothTest.java +++ b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothTest.java @@ -4,10 +4,6 @@ import java.util.UUID; class BluetoothTest { - private static final String EMPTY_UUID = - UUID.nameUUIDFromBytes(new byte[0]).toString().replaceAll("-", ""); - - static String getUuid() { - return EMPTY_UUID; - } + static final String EMPTY_UUID = + UUID.nameUUIDFromBytes(new byte[0]).toString(); } \ No newline at end of file