Encode transport properties more compactly in QR codes.

This commit is contained in:
akwizgran
2016-11-07 16:25:30 +00:00
parent 7327029fca
commit 04d4ecad05
20 changed files with 326 additions and 168 deletions

View File

@@ -11,12 +11,13 @@ import android.content.IntentFilter;
import org.briarproject.android.api.AndroidExecutor; import org.briarproject.android.api.AndroidExecutor;
import org.briarproject.android.util.AndroidUtils; import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.PseudoRandom; import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.keyagreement.KeyAgreementConnection; import org.briarproject.api.keyagreement.KeyAgreementConnection;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff; import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
@@ -44,6 +45,8 @@ import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED; import static android.bluetooth.BluetoothAdapter.ACTION_SCAN_MODE_CHANGED;
import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED; import static android.bluetooth.BluetoothAdapter.ACTION_STATE_CHANGED;
import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE; import static android.bluetooth.BluetoothAdapter.EXTRA_SCAN_MODE;
@@ -57,6 +60,7 @@ import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.util.PrivacyUtils.scrubMacAddress; import static org.briarproject.util.PrivacyUtils.scrubMacAddress;
class DroidtoothPlugin implements DuplexPlugin { class DroidtoothPlugin implements DuplexPlugin {
@@ -466,23 +470,25 @@ class DroidtoothPlugin implements DuplexPlugin {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null; return null;
} }
TransportProperties p = new TransportProperties(); BdfList descriptor = new BdfList();
descriptor.add(TRANSPORT_ID_BLUETOOTH);
String address = AndroidUtils.getBluetoothAddress(appContext, adapter); String address = AndroidUtils.getBluetoothAddress(appContext, adapter);
if (!StringUtils.isNullOrEmpty(address)) if (!address.isEmpty()) descriptor.add(StringUtils.macToBytes(address));
p.put(PROP_ADDRESS, address); return new BluetoothKeyAgreementListener(descriptor, ss);
TransportDescriptor d = new TransportDescriptor(ID, p);
return new BluetoothKeyAgreementListener(d, ss);
} }
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, TransportDescriptor d, long timeout) { byte[] commitment, BdfList descriptor, long timeout) {
if (!isRunning()) return null; if (!isRunning()) return null;
if (!ID.equals(d.getIdentifier())) return null; String address;
TransportProperties p = d.getProperties(); try {
if (p == null) return null; address = parseAddress(descriptor);
String address = p.get(PROP_ADDRESS); } catch (FormatException e) {
if (StringUtils.isNullOrEmpty(address)) return null; LOG.info("Invalid address in key agreement descriptor");
return null;
}
if (address == null) return null;
// No truncation necessary because COMMIT_LENGTH = 16 // No truncation necessary because COMMIT_LENGTH = 16
UUID uuid = UUID.nameUUIDFromBytes(commitment); UUID uuid = UUID.nameUUIDFromBytes(commitment);
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -492,6 +498,14 @@ class DroidtoothPlugin implements DuplexPlugin {
return new DroidtoothTransportConnection(this, s); return new DroidtoothTransportConnection(this, s);
} }
@Nullable
private String parseAddress(BdfList descriptor) throws FormatException {
if (descriptor.size() < 2) return null;
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac);
}
private class BluetoothStateReceiver extends BroadcastReceiver { private class BluetoothStateReceiver extends BroadcastReceiver {
@Override @Override
@@ -626,7 +640,7 @@ class DroidtoothPlugin implements DuplexPlugin {
private final BluetoothServerSocket ss; private final BluetoothServerSocket ss;
BluetoothKeyAgreementListener(TransportDescriptor descriptor, BluetoothKeyAgreementListener(BdfList descriptor,
BluetoothServerSocket ss) { BluetoothServerSocket ss) {
super(descriptor); super(descriptor);
this.ss = ss; this.ss = ss;

View File

@@ -19,11 +19,11 @@ import org.briarproject.android.util.AndroidUtils;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.PseudoRandom; import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.event.Event; import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener; import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.SettingsUpdatedEvent; import org.briarproject.api.event.SettingsUpdatedEvent;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff; import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.TorConstants; import org.briarproject.api.plugins.TorConstants;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
@@ -574,7 +574,7 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, TransportDescriptor d, long timeout) { byte[] commitment, BdfList descriptor, long timeout) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@@ -4,7 +4,7 @@ package org.briarproject.api.keyagreement;
public interface KeyAgreementConstants { public interface KeyAgreementConstants {
/** The current version of the BQP protocol. */ /** The current version of the BQP protocol. */
byte PROTOCOL_VERSION = 1; byte PROTOCOL_VERSION = 2;
/** The length of the record header in bytes. */ /** The length of the record header in bytes. */
int RECORD_HEADER_LENGTH = 4; int RECORD_HEADER_LENGTH = 4;
@@ -16,4 +16,10 @@ public interface KeyAgreementConstants {
int COMMIT_LENGTH = 16; int COMMIT_LENGTH = 16;
long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
/** The transport identifier for Bluetooth. */
int TRANSPORT_ID_BLUETOOTH = 0;
/** The transport identifier for LAN. */
int TRANSPORT_ID_LAN = 1;
} }

View File

@@ -1,5 +1,7 @@
package org.briarproject.api.keyagreement; package org.briarproject.api.keyagreement;
import org.briarproject.api.data.BdfList;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
/** /**
@@ -7,9 +9,9 @@ import java.util.concurrent.Callable;
*/ */
public abstract class KeyAgreementListener { public abstract class KeyAgreementListener {
private final TransportDescriptor descriptor; private final BdfList descriptor;
public KeyAgreementListener(TransportDescriptor descriptor) { public KeyAgreementListener(BdfList descriptor) {
this.descriptor = descriptor; this.descriptor = descriptor;
} }
@@ -17,7 +19,7 @@ public abstract class KeyAgreementListener {
* Returns the descriptor that a remote peer can use to connect to this * Returns the descriptor that a remote peer can use to connect to this
* listener. * listener.
*/ */
public TransportDescriptor getDescriptor() { public BdfList getDescriptor() {
return descriptor; return descriptor;
} }

View File

@@ -1,29 +1,40 @@
package org.briarproject.api.keyagreement; package org.briarproject.api.keyagreement;
import org.briarproject.api.Bytes; import org.briarproject.api.Bytes;
import org.briarproject.api.TransportId;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.nullsafety.NotNullByDefault;
import java.util.List; import java.util.Map;
import javax.annotation.concurrent.Immutable;
/** /**
* A BQP payload. * A BQP payload.
*/ */
@Immutable
@NotNullByDefault
public class Payload implements Comparable<Payload> { public class Payload implements Comparable<Payload> {
private final Bytes commitment; private final Bytes commitment;
private final List<TransportDescriptor> descriptors; private final Map<TransportId, BdfList> descriptors;
public Payload(byte[] commitment, List<TransportDescriptor> descriptors) { public Payload(byte[] commitment, Map<TransportId, BdfList> descriptors) {
this.commitment = new Bytes(commitment); this.commitment = new Bytes(commitment);
this.descriptors = descriptors; this.descriptors = descriptors;
} }
/** Returns the commitment contained in this payload. */ /**
* Returns the commitment contained in this payload.
*/
public byte[] getCommitment() { public byte[] getCommitment() {
return commitment.getBytes(); return commitment.getBytes();
} }
/** Returns the transport descriptors contained in this payload. */ /**
public List<TransportDescriptor> getTransportDescriptors() { * Returns the transport descriptors contained in this payload.
*/
public Map<TransportId, BdfList> getTransportDescriptors() {
return descriptors; return descriptors;
} }

View File

@@ -1,7 +1,10 @@
package org.briarproject.api.keyagreement; package org.briarproject.api.keyagreement;
import org.briarproject.api.nullsafety.NotNullByDefault;
import java.io.IOException; import java.io.IOException;
@NotNullByDefault
public interface PayloadParser { public interface PayloadParser {
Payload parse(byte[] raw) throws IOException; Payload parse(byte[] raw) throws IOException;

View File

@@ -1,28 +0,0 @@
package org.briarproject.api.keyagreement;
import org.briarproject.api.TransportId;
import org.briarproject.api.properties.TransportProperties;
/**
* Describes how to connect to a device over a short-range transport.
*/
public class TransportDescriptor {
private final TransportId id;
private final TransportProperties properties;
public TransportDescriptor(TransportId id, TransportProperties properties) {
this.id = id;
this.properties = properties;
}
/** Returns the transport identifier. */
public TransportId getIdentifier() {
return id;
}
/** Returns the transport properties. */
public TransportProperties getProperties() {
return properties;
}
}

View File

@@ -1,21 +1,26 @@
package org.briarproject.api.plugins; package org.briarproject.api.plugins;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.simplex.SimplexPlugin; import org.briarproject.api.plugins.simplex.SimplexPlugin;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable;
/** /**
* Responsible for starting transport plugins at startup and stopping them at * Responsible for starting transport plugins at startup and stopping them at
* shutdown. * shutdown.
*/ */
@NotNullByDefault
public interface PluginManager { public interface PluginManager {
/** /**
* Returns the plugin for the given transport, or null if no such plugin * Returns the plugin for the given transport, or null if no such plugin
* has been created. * has been created.
*/ */
@Nullable
Plugin getPlugin(TransportId t); Plugin getPlugin(TransportId t);
/** /**

View File

@@ -2,8 +2,8 @@ package org.briarproject.api.plugins.duplex;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.PseudoRandom; import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.Plugin;
/** An interface for transport plugins that support duplex communication. */ /** An interface for transport plugins that support duplex communication. */
@@ -40,5 +40,5 @@ public interface DuplexPlugin extends Plugin {
* Returns null if no connection can be established within the given time. * Returns null if no connection can be established within the given time.
*/ */
DuplexTransportConnection createKeyAgreementConnection( DuplexTransportConnection createKeyAgreementConnection(
byte[] remoteCommitment, TransportDescriptor d, long timeout); byte[] remoteCommitment, BdfList descriptor, long timeout);
} }

View File

@@ -1,11 +1,13 @@
package org.briarproject.keyagreement; package org.briarproject.keyagreement;
import org.briarproject.api.TransportId;
import org.briarproject.api.crypto.CryptoComponent; import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair; import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.keyagreement.KeyAgreementConnection; import org.briarproject.api.keyagreement.KeyAgreementConnection;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.Payload; import org.briarproject.api.keyagreement.Payload;
import org.briarproject.api.keyagreement.TransportDescriptor; import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginManager; import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
@@ -14,7 +16,10 @@ import org.briarproject.api.system.Clock;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.CompletionService; import java.util.concurrent.CompletionService;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
@@ -68,14 +73,13 @@ class KeyAgreementConnector {
byte[] commitment = crypto.deriveKeyCommitment( byte[] commitment = crypto.deriveKeyCommitment(
localKeyPair.getPublic().getEncoded()); localKeyPair.getPublic().getEncoded());
// Start all listeners and collect their descriptors // Start all listeners and collect their descriptors
List<TransportDescriptor> descriptors = Map<TransportId, BdfList> descriptors =
new ArrayList<TransportDescriptor>(); new HashMap<TransportId, BdfList>();
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) { for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
KeyAgreementListener l = plugin.createKeyAgreementListener( KeyAgreementListener l =
commitment); plugin.createKeyAgreementListener(commitment);
if (l != null) { if (l != null) {
TransportDescriptor d = l.getDescriptor(); descriptors.put(plugin.getId(), l.getDescriptor());
descriptors.add(d);
pending.add(connect.submit(new ReadableTask(l.listen()))); pending.add(connect.submit(new ReadableTask(l.listen())));
listeners.add(l); listeners.add(l);
} }
@@ -100,13 +104,16 @@ class KeyAgreementConnector {
// Start connecting over supported transports // Start connecting over supported transports
LOG.info("Starting outgoing BQP connections"); LOG.info("Starting outgoing BQP connections");
for (TransportDescriptor d : remotePayload.getTransportDescriptors()) { Map<TransportId, BdfList> descriptors =
DuplexPlugin plugin = (DuplexPlugin) pluginManager.getPlugin( remotePayload.getTransportDescriptors();
d.getIdentifier()); for (Entry<TransportId, BdfList> e : descriptors.entrySet()) {
if (plugin != null) Plugin p = pluginManager.getPlugin(e.getKey());
if (p instanceof DuplexPlugin) {
DuplexPlugin plugin = (DuplexPlugin) p;
pending.add(connect.submit(new ReadableTask( pending.add(connect.submit(new ReadableTask(
new ConnectorTask(plugin, remotePayload.getCommitment(), new ConnectorTask(plugin, remotePayload.getCommitment(),
d, end)))); e.getValue(), end))));
}
} }
// Get chosen connection // Get chosen connection
@@ -170,12 +177,12 @@ class KeyAgreementConnector {
private class ConnectorTask implements Callable<KeyAgreementConnection> { private class ConnectorTask implements Callable<KeyAgreementConnection> {
private final byte[] commitment; private final byte[] commitment;
private final TransportDescriptor descriptor; private final BdfList descriptor;
private final long end; private final long end;
private final DuplexPlugin plugin; private final DuplexPlugin plugin;
private ConnectorTask(DuplexPlugin plugin, byte[] commitment, private ConnectorTask(DuplexPlugin plugin, byte[] commitment,
TransportDescriptor descriptor, long end) { BdfList descriptor, long end) {
this.plugin = plugin; this.plugin = plugin;
this.commitment = commitment; this.commitment = commitment;
this.descriptor = descriptor; this.descriptor = descriptor;

View File

@@ -1,24 +1,30 @@
package org.briarproject.keyagreement; package org.briarproject.keyagreement;
import org.briarproject.api.TransportId;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfWriter; import org.briarproject.api.data.BdfWriter;
import org.briarproject.api.data.BdfWriterFactory; import org.briarproject.api.data.BdfWriterFactory;
import org.briarproject.api.keyagreement.Payload; import org.briarproject.api.keyagreement.Payload;
import org.briarproject.api.keyagreement.PayloadEncoder; import org.briarproject.api.keyagreement.PayloadEncoder;
import org.briarproject.api.keyagreement.TransportDescriptor; import org.briarproject.api.nullsafety.NotNullByDefault;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
@Immutable
@NotNullByDefault
class PayloadEncoderImpl implements PayloadEncoder { class PayloadEncoderImpl implements PayloadEncoder {
private final BdfWriterFactory bdfWriterFactory; private final BdfWriterFactory bdfWriterFactory;
@Inject @Inject
public PayloadEncoderImpl(BdfWriterFactory bdfWriterFactory) { PayloadEncoderImpl(BdfWriterFactory bdfWriterFactory) {
this.bdfWriterFactory = bdfWriterFactory; this.bdfWriterFactory = bdfWriterFactory;
} }
@@ -30,18 +36,13 @@ class PayloadEncoderImpl implements PayloadEncoder {
w.writeListStart(); // Payload start w.writeListStart(); // Payload start
w.writeLong(PROTOCOL_VERSION); w.writeLong(PROTOCOL_VERSION);
w.writeRaw(p.getCommitment()); w.writeRaw(p.getCommitment());
w.writeListStart(); // Descriptors start Map<TransportId, BdfList> descriptors = p.getTransportDescriptors();
for (TransportDescriptor d : p.getTransportDescriptors()) { for (BdfList descriptor : descriptors.values())
w.writeListStart(); w.writeList(descriptor);
w.writeString(d.getIdentifier().getString());
w.writeDictionary(d.getProperties());
w.writeListEnd();
}
w.writeListEnd(); // Descriptors end
w.writeListEnd(); // Payload end w.writeListEnd(); // Payload end
} catch (IOException e) { } catch (IOException e) {
// Shouldn't happen with ByteArrayOutputStream // Shouldn't happen with ByteArrayOutputStream
throw new RuntimeException(e); throw new AssertionError(e);
} }
return out.toByteArray(); return out.toByteArray();
} }

View File

@@ -2,30 +2,34 @@ package org.briarproject.keyagreement;
import org.briarproject.api.FormatException; import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.data.BdfReader; import org.briarproject.api.data.BdfReader;
import org.briarproject.api.data.BdfReaderFactory; import org.briarproject.api.data.BdfReaderFactory;
import org.briarproject.api.keyagreement.Payload; import org.briarproject.api.keyagreement.Payload;
import org.briarproject.api.keyagreement.PayloadParser; import org.briarproject.api.keyagreement.PayloadParser;
import org.briarproject.api.keyagreement.TransportDescriptor; import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.properties.TransportProperties;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.HashMap;
import java.util.List; import java.util.Map;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION; import static org.briarproject.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.api.properties.TransportPropertyConstants.MAX_PROPERTY_LENGTH; import static org.briarproject.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
@Immutable
@NotNullByDefault
class PayloadParserImpl implements PayloadParser { class PayloadParserImpl implements PayloadParser {
private final BdfReaderFactory bdfReaderFactory; private final BdfReaderFactory bdfReaderFactory;
@Inject @Inject
public PayloadParserImpl(BdfReaderFactory bdfReaderFactory) { PayloadParserImpl(BdfReaderFactory bdfReaderFactory) {
this.bdfReaderFactory = bdfReaderFactory; this.bdfReaderFactory = bdfReaderFactory;
} }
@@ -33,36 +37,28 @@ class PayloadParserImpl implements PayloadParser {
public Payload parse(byte[] raw) throws IOException { public Payload parse(byte[] raw) throws IOException {
ByteArrayInputStream in = new ByteArrayInputStream(raw); ByteArrayInputStream in = new ByteArrayInputStream(raw);
BdfReader r = bdfReaderFactory.createReader(in); BdfReader r = bdfReaderFactory.createReader(in);
r.readListStart(); // Payload start // The payload is a BDF list with two or more elements
int proto = (int) r.readLong(); BdfList payload = r.readList();
if (proto != PROTOCOL_VERSION) if (payload.size() < 2) throw new FormatException();
throw new FormatException(); if (!r.eof()) throw new FormatException();
byte[] commitment = r.readRaw(COMMIT_LENGTH); // First element: the protocol version
if (commitment.length != COMMIT_LENGTH) long protocolVersion = payload.getLong(0);
throw new FormatException(); if (protocolVersion != PROTOCOL_VERSION) throw new FormatException();
List<TransportDescriptor> descriptors = new ArrayList<TransportDescriptor>(); // Second element: the public key commitment
r.readListStart(); // Descriptors start byte[] commitment = payload.getRaw(1);
while (r.hasList()) { if (commitment.length != COMMIT_LENGTH) throw new FormatException();
r.readListStart(); // Remaining elements: transport descriptors
while (!r.hasListEnd()) { Map<TransportId, BdfList> recognised =
TransportId id = new HashMap<TransportId, BdfList>();
new TransportId(r.readString(MAX_PROPERTY_LENGTH)); for (int i = 2; i < payload.size(); i++) {
TransportProperties p = new TransportProperties(); BdfList descriptor = payload.getList(i);
r.readDictionaryStart(); long transportId = descriptor.getLong(0);
while (!r.hasDictionaryEnd()) { if (transportId == TRANSPORT_ID_BLUETOOTH) {
String key = r.readString(MAX_PROPERTY_LENGTH); recognised.put(new TransportId("bt"), descriptor);
String value = r.readString(MAX_PROPERTY_LENGTH); } else if (transportId == TRANSPORT_ID_LAN) {
p.put(key, value); recognised.put(new TransportId("lan"), descriptor);
}
r.readDictionaryEnd();
descriptors.add(new TransportDescriptor(id, p));
} }
r.readListEnd();
} }
r.readListEnd(); // Descriptors end return new Payload(commitment, recognised);
r.readListEnd(); // Payload end
if (!r.eof())
throw new FormatException();
return new Payload(commitment, descriptors);
} }
} }

View File

@@ -9,6 +9,7 @@ import org.briarproject.api.event.TransportEnabledEvent;
import org.briarproject.api.lifecycle.IoExecutor; import org.briarproject.api.lifecycle.IoExecutor;
import org.briarproject.api.lifecycle.Service; import org.briarproject.api.lifecycle.Service;
import org.briarproject.api.lifecycle.ServiceException; import org.briarproject.api.lifecycle.ServiceException;
import org.briarproject.api.nullsafety.NotNullByDefault;
import org.briarproject.api.plugins.ConnectionManager; import org.briarproject.api.plugins.ConnectionManager;
import org.briarproject.api.plugins.Plugin; import org.briarproject.api.plugins.Plugin;
import org.briarproject.api.plugins.PluginCallback; import org.briarproject.api.plugins.PluginCallback;
@@ -42,11 +43,14 @@ import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@ThreadSafe
@NotNullByDefault
class PluginManagerImpl implements PluginManager, Service { class PluginManagerImpl implements PluginManager, Service {
private static final Logger LOG = private static final Logger LOG =

View File

@@ -1,10 +1,11 @@
package org.briarproject.plugins.tcp; package org.briarproject.plugins.tcp;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.keyagreement.KeyAgreementConnection; import org.briarproject.api.keyagreement.KeyAgreementConnection;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff; import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
import org.briarproject.api.plugins.duplex.DuplexTransportConnection; import org.briarproject.api.plugins.duplex.DuplexTransportConnection;
@@ -19,6 +20,7 @@ import java.net.InetSocketAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.LinkedList; import java.util.LinkedList;
@@ -27,8 +29,12 @@ import java.util.concurrent.Callable;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
import static org.briarproject.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.util.PrivacyUtils.scrubSocketAddress; import static org.briarproject.util.PrivacyUtils.scrubSocketAddress;
class LanTcpPlugin extends TcpPlugin { class LanTcpPlugin extends TcpPlugin {
@@ -40,7 +46,6 @@ class LanTcpPlugin extends TcpPlugin {
private static final int MAX_ADDRESSES = 5; private static final int MAX_ADDRESSES = 5;
private static final String PROP_IP_PORTS = "ipPorts"; private static final String PROP_IP_PORTS = "ipPorts";
private static final String PROP_IP_PORT = "ipPort";
private static final String SEPARATOR = ","; private static final String SEPARATOR = ",";
LanTcpPlugin(Executor ioExecutor, Backoff backoff, LanTcpPlugin(Executor ioExecutor, Backoff backoff,
@@ -186,22 +191,26 @@ class LanTcpPlugin extends TcpPlugin {
LOG.info("Could not bind server socket for key agreement"); LOG.info("Could not bind server socket for key agreement");
return null; return null;
} }
TransportProperties p = new TransportProperties(); BdfList descriptor = new BdfList();
SocketAddress local = ss.getLocalSocketAddress(); descriptor.add(TRANSPORT_ID_LAN);
p.put(PROP_IP_PORT, getIpPortString((InetSocketAddress) local)); InetSocketAddress local =
TransportDescriptor d = new TransportDescriptor(ID, p); (InetSocketAddress) ss.getLocalSocketAddress();
return new LanKeyAgreementListener(d, ss); descriptor.add(local.getAddress().getAddress());
descriptor.add(local.getPort());
return new LanKeyAgreementListener(descriptor, ss);
} }
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, TransportDescriptor d, long timeout) { byte[] commitment, BdfList descriptor, long timeout) {
if (!isRunning()) return null; if (!isRunning()) return null;
if (!ID.equals(d.getIdentifier())) return null; InetSocketAddress remote;
TransportProperties p = d.getProperties(); try {
if (p == null) return null; remote = parseSocketAddress(descriptor);
String ipPort = p.get(PROP_IP_PORT); } catch (FormatException e) {
InetSocketAddress remote = parseSocketAddress(ipPort); LOG.info("Invalid IP/port in key agreement descriptor");
return null;
}
if (remote == null) return null; if (remote == null) return null;
if (!isConnectable(remote)) { if (!isConnectable(remote)) {
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
@@ -228,11 +237,27 @@ class LanTcpPlugin extends TcpPlugin {
} }
} }
@Nullable
private InetSocketAddress parseSocketAddress(BdfList descriptor)
throws FormatException {
if (descriptor.size() < 3) return null;
byte[] address = descriptor.getRaw(1);
int port = descriptor.getLong(2).intValue();
if (port < 1 || port > MAX_16_BIT_UNSIGNED) throw new FormatException();
try {
InetAddress addr = InetAddress.getByAddress(address);
return new InetSocketAddress(addr, port);
} catch (UnknownHostException e) {
// Invalid address length
throw new FormatException();
}
}
private class LanKeyAgreementListener extends KeyAgreementListener { private class LanKeyAgreementListener extends KeyAgreementListener {
private final ServerSocket ss; private final ServerSocket ss;
public LanKeyAgreementListener(TransportDescriptor descriptor, private LanKeyAgreementListener(BdfList descriptor,
ServerSocket ss) { ServerSocket ss) {
super(descriptor); super(descriptor);
this.ss = ss; this.ss = ss;

View File

@@ -2,8 +2,8 @@ package org.briarproject.plugins.tcp;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.PseudoRandom; import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff; import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
@@ -298,7 +298,7 @@ abstract class TcpPlugin implements DuplexPlugin {
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, TransportDescriptor d, long timeout) { byte[] commitment, BdfList descriptor, long timeout) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@@ -1,24 +1,34 @@
package org.briarproject.util; package org.briarproject.util;
import org.briarproject.api.nullsafety.NotNullByDefault;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException; import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetDecoder;
import java.util.Collection; import java.util.Collection;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import static java.nio.charset.CodingErrorAction.IGNORE; import static java.nio.charset.CodingErrorAction.IGNORE;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
@NotNullByDefault
public class StringUtils { public class StringUtils {
private static final Charset UTF_8 = Charset.forName("UTF-8"); private static final Charset UTF_8 = Charset.forName("UTF-8");
private static Pattern MAC = Pattern.compile("[0-9a-f]{2}:[0-9a-f]{2}:" +
"[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}",
CASE_INSENSITIVE);
private static final char[] HEX = new char[] { private static final char[] HEX = new char[] {
'0', '1', '2', '3', '4', '5', '6', '7', '0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F' '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
}; };
public static boolean isNullOrEmpty(String s) { public static boolean isNullOrEmpty(@Nullable String s) {
return s == null || s.length() == 0; return s == null || s.length() == 0;
} }
@@ -61,7 +71,9 @@ public class StringUtils {
return fromUtf8(utf8, 0, maxUtf8Length); return fromUtf8(utf8, 0, maxUtf8Length);
} }
/** Converts the given byte array to a hex character array. */ /**
* Converts the given byte array to a hex character array.
*/
private static char[] toHexChars(byte[] bytes) { private static char[] toHexChars(byte[] bytes) {
char[] hex = new char[bytes.length * 2]; char[] hex = new char[bytes.length * 2];
for (int i = 0, j = 0; i < bytes.length; i++) { for (int i = 0, j = 0; i < bytes.length; i++) {
@@ -71,12 +83,16 @@ public class StringUtils {
return hex; return hex;
} }
/** Converts the given byte array to a hex string. */ /**
* Converts the given byte array to a hex string.
*/
public static String toHexString(byte[] bytes) { public static String toHexString(byte[] bytes) {
return new String(toHexChars(bytes)); return new String(toHexChars(bytes));
} }
/** Converts the given hex string to a byte array. */ /**
* Converts the given hex string to a byte array.
*/
public static byte[] fromHexString(String hex) { public static byte[] fromHexString(String hex) {
int len = hex.length(); int len = hex.length();
if (len % 2 != 0) if (len % 2 != 0)
@@ -107,4 +123,20 @@ public class StringUtils {
public static boolean utf8IsTooLong(String s, int maxLength) { public static boolean utf8IsTooLong(String s, int maxLength) {
return toUtf8(s).length > maxLength; return toUtf8(s).length > maxLength;
} }
public static byte[] macToBytes(String mac) {
if (!MAC.matcher(mac).matches()) throw new IllegalArgumentException();
return fromHexString(mac.replaceAll(":", ""));
}
public static String macToString(byte[] mac) {
if (mac.length != 6) throw new IllegalArgumentException();
StringBuilder s = new StringBuilder();
for (byte b : mac) {
if (s.length() > 0) s.append(':');
s.append(HEX[(b >> 4) & 0xF]);
s.append(HEX[b & 0xF]);
}
return s.toString();
}
} }

View File

@@ -1,11 +1,12 @@
package org.briarproject.plugins.bluetooth; package org.briarproject.plugins.bluetooth;
import org.briarproject.api.FormatException;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.PseudoRandom; import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.keyagreement.KeyAgreementConnection; import org.briarproject.api.keyagreement.KeyAgreementConnection;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff; import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
@@ -33,6 +34,7 @@ import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.bluetooth.BluetoothStateException; import javax.bluetooth.BluetoothStateException;
import javax.bluetooth.DiscoveryAgent; import javax.bluetooth.DiscoveryAgent;
import javax.bluetooth.LocalDevice; import javax.bluetooth.LocalDevice;
@@ -44,6 +46,7 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static javax.bluetooth.DiscoveryAgent.GIAC; import static javax.bluetooth.DiscoveryAgent.GIAC;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
class BluetoothPlugin implements DuplexPlugin { class BluetoothPlugin implements DuplexPlugin {
@@ -382,21 +385,25 @@ class BluetoothPlugin implements DuplexPlugin {
tryToClose(ss); tryToClose(ss);
return null; return null;
} }
TransportProperties p = new TransportProperties(); BdfList descriptor = new BdfList();
p.put(PROP_ADDRESS, localDevice.getBluetoothAddress()); descriptor.add(TRANSPORT_ID_BLUETOOTH);
TransportDescriptor d = new TransportDescriptor(ID, p); String address = localDevice.getBluetoothAddress();
return new BluetoothKeyAgreementListener(d, ss); if (!address.isEmpty()) descriptor.add(StringUtils.macToBytes(address));
return new BluetoothKeyAgreementListener(descriptor, ss);
} }
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, TransportDescriptor d, long timeout) { byte[] commitment, BdfList descriptor, long timeout) {
if (!isRunning()) return null; if (!isRunning()) return null;
if (!ID.equals(d.getIdentifier())) return null; String address;
TransportProperties p = d.getProperties(); try {
if (p == null) return null; address = parseAddress(descriptor);
String address = p.get(PROP_ADDRESS); } catch (FormatException e) {
if (StringUtils.isNullOrEmpty(address)) return null; LOG.info("Invalid address in key agreement descriptor");
return null;
}
if (address == null) return null;
// No truncation necessary because COMMIT_LENGTH = 16 // No truncation necessary because COMMIT_LENGTH = 16
String uuid = UUID.nameUUIDFromBytes(commitment).toString(); String uuid = UUID.nameUUIDFromBytes(commitment).toString();
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -407,6 +414,14 @@ class BluetoothPlugin implements DuplexPlugin {
return new BluetoothTransportConnection(this, s); return new BluetoothTransportConnection(this, s);
} }
@Nullable
private String parseAddress(BdfList descriptor) throws FormatException {
if (descriptor.size() < 2) return null;
byte[] mac = descriptor.getRaw(1);
if (mac.length != 6) throw new FormatException();
return StringUtils.macToString(mac);
}
private void makeDeviceDiscoverable() { private void makeDeviceDiscoverable() {
// Try to make the device discoverable (requires root on Linux) // Try to make the device discoverable (requires root on Linux)
try { try {
@@ -491,7 +506,7 @@ class BluetoothPlugin implements DuplexPlugin {
private final StreamConnectionNotifier ss; private final StreamConnectionNotifier ss;
BluetoothKeyAgreementListener(TransportDescriptor descriptor, BluetoothKeyAgreementListener(BdfList descriptor,
StreamConnectionNotifier ss) { StreamConnectionNotifier ss) {
super(descriptor); super(descriptor);
this.ss = ss; this.ss = ss;

View File

@@ -3,8 +3,8 @@ package org.briarproject.plugins.modem;
import org.briarproject.api.TransportId; import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.crypto.PseudoRandom; import org.briarproject.api.crypto.PseudoRandom;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.duplex.AbstractDuplexTransportConnection; import org.briarproject.api.plugins.duplex.AbstractDuplexTransportConnection;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
@@ -114,7 +114,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
boolean resetModem() { private boolean resetModem() {
if (!running) return false; if (!running) return false;
for (String portName : serialPortList.getPortNames()) { for (String portName : serialPortList.getPortNames()) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -184,7 +184,7 @@ class ModemPlugin implements DuplexPlugin, Modem.Callback {
@Override @Override
public DuplexTransportConnection createKeyAgreementConnection( public DuplexTransportConnection createKeyAgreementConnection(
byte[] commitment, TransportDescriptor d, long timeout) { byte[] commitment, BdfList descriptor, long timeout) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@@ -2,9 +2,9 @@ package org.briarproject.plugins.tcp;
import org.briarproject.BriarTestCase; import org.briarproject.BriarTestCase;
import org.briarproject.api.contact.ContactId; import org.briarproject.api.contact.ContactId;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.keyagreement.KeyAgreementConnection; import org.briarproject.api.keyagreement.KeyAgreementConnection;
import org.briarproject.api.keyagreement.KeyAgreementListener; import org.briarproject.api.keyagreement.KeyAgreementListener;
import org.briarproject.api.keyagreement.TransportDescriptor;
import org.briarproject.api.plugins.Backoff; import org.briarproject.api.plugins.Backoff;
import org.briarproject.api.plugins.duplex.DuplexPlugin; import org.briarproject.api.plugins.duplex.DuplexPlugin;
import org.briarproject.api.plugins.duplex.DuplexPluginCallback; import org.briarproject.api.plugins.duplex.DuplexPluginCallback;
@@ -27,11 +27,11 @@ import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import static java.util.concurrent.TimeUnit.SECONDS; import static java.util.concurrent.TimeUnit.SECONDS;
import static org.briarproject.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_LAN;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@@ -193,18 +193,15 @@ public class LanTcpPluginTest extends BriarTestCase {
FutureTask<KeyAgreementConnection> f = new FutureTask<>(c); FutureTask<KeyAgreementConnection> f = new FutureTask<>(c);
new Thread(f).start(); new Thread(f).start();
// The plugin should have bound a socket and stored the port number // The plugin should have bound a socket and stored the port number
TransportDescriptor d = kal.getDescriptor(); BdfList descriptor = kal.getDescriptor();
TransportProperties p = d.getProperties(); assertEquals(3, descriptor.size());
String ipPort = p.get("ipPort"); assertEquals(TRANSPORT_ID_LAN, descriptor.getLong(0).longValue());
assertNotNull(ipPort); byte[] address = descriptor.getRaw(1);
String[] split = ipPort.split(":"); InetAddress addr = InetAddress.getByAddress(address);
assertEquals(2, split.length);
String addrString = split[0], portString = split[1];
InetAddress addr = InetAddress.getByName(addrString);
assertTrue(addr instanceof Inet4Address); assertTrue(addr instanceof Inet4Address);
assertFalse(addr.isLoopbackAddress()); assertFalse(addr.isLoopbackAddress());
assertTrue(addr.isLinkLocalAddress() || addr.isSiteLocalAddress()); assertTrue(addr.isLinkLocalAddress() || addr.isSiteLocalAddress());
int port = Integer.parseInt(portString); int port = descriptor.getLong(2).intValue();
assertTrue(port > 0 && port < 65536); assertTrue(port > 0 && port < 65536);
// The plugin should be listening on the port // The plugin should be listening on the port
InetSocketAddress socketAddr = new InetSocketAddress(addr, port); InetSocketAddress socketAddr = new InetSocketAddress(addr, port);
@@ -240,7 +237,6 @@ public class LanTcpPluginTest extends BriarTestCase {
// Listen on the same interface as the plugin // Listen on the same interface as the plugin
final ServerSocket ss = new ServerSocket(); final ServerSocket ss = new ServerSocket();
ss.bind(new InetSocketAddress(addrString, 0), 10); ss.bind(new InetSocketAddress(addrString, 0), 10);
int port = ss.getLocalPort();
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
final AtomicBoolean error = new AtomicBoolean(false); final AtomicBoolean error = new AtomicBoolean(false);
new Thread() { new Thread() {
@@ -255,12 +251,15 @@ public class LanTcpPluginTest extends BriarTestCase {
} }
}.start(); }.start();
// Tell the plugin about the port // Tell the plugin about the port
TransportProperties p = new TransportProperties(); BdfList descriptor = new BdfList();
p.put("ipPort", addrString + ":" + port); descriptor.add(TRANSPORT_ID_LAN);
TransportDescriptor desc = new TransportDescriptor(plugin.getId(), p); InetSocketAddress local =
(InetSocketAddress) ss.getLocalSocketAddress();
descriptor.add(local.getAddress().getAddress());
descriptor.add(local.getPort());
// Connect to the port // Connect to the port
DuplexTransportConnection d = DuplexTransportConnection d =
plugin.createKeyAgreementConnection(null, desc, 5000); plugin.createKeyAgreementConnection(null, descriptor, 5000);
assertNotNull(d); assertNotNull(d);
// Check that the connection was accepted // Check that the connection was accepted
assertTrue(latch.await(5, SECONDS)); assertTrue(latch.await(5, SECONDS));
@@ -291,61 +290,76 @@ public class LanTcpPluginTest extends BriarTestCase {
private final CountDownLatch connectionsLatch = new CountDownLatch(1); private final CountDownLatch connectionsLatch = new CountDownLatch(1);
private final TransportProperties local = new TransportProperties(); private final TransportProperties local = new TransportProperties();
@Override
public Settings getSettings() { public Settings getSettings() {
return new Settings(); return new Settings();
} }
@Override
public TransportProperties getLocalProperties() { public TransportProperties getLocalProperties() {
return local; return local;
} }
@Override
public Map<ContactId, TransportProperties> getRemoteProperties() { public Map<ContactId, TransportProperties> getRemoteProperties() {
return remote; return remote;
} }
@Override
public void mergeSettings(Settings s) { public void mergeSettings(Settings s) {
} }
@Override
public void mergeLocalProperties(TransportProperties p) { public void mergeLocalProperties(TransportProperties p) {
local.putAll(p); local.putAll(p);
propertiesLatch.countDown(); propertiesLatch.countDown();
} }
@Override
public int showChoice(String[] options, String... message) { public int showChoice(String[] options, String... message) {
return -1; return -1;
} }
@Override
public boolean showConfirmationMessage(String... message) { public boolean showConfirmationMessage(String... message) {
return false; return false;
} }
@Override
public void showMessage(String... message) { public void showMessage(String... message) {
} }
@Override
public void incomingConnectionCreated(DuplexTransportConnection d) { public void incomingConnectionCreated(DuplexTransportConnection d) {
connectionsLatch.countDown(); connectionsLatch.countDown();
} }
@Override
public void outgoingConnectionCreated(ContactId c, public void outgoingConnectionCreated(ContactId c,
DuplexTransportConnection d) { DuplexTransportConnection d) {
} }
@Override
public void transportEnabled() { public void transportEnabled() {
} }
@Override
public void transportDisabled() { public void transportDisabled() {
} }
} }
private static class TestBackoff implements Backoff { private static class TestBackoff implements Backoff {
@Override
public int getPollingInterval() { public int getPollingInterval() {
return 60 * 1000; return 60 * 1000;
} }
@Override
public void increment() { public void increment() {
} }
@Override
public void reset() { public void reset() {
} }
} }

View File

@@ -173,4 +173,55 @@ public class StringUtilsTest extends BriarTestCase {
public void testTruncateUtf8EmptyInput() { public void testTruncateUtf8EmptyInput() {
assertEquals("", StringUtils.truncateUtf8("", 123)); assertEquals("", StringUtils.truncateUtf8("", 123));
} }
@Test(expected = IllegalArgumentException.class)
public void testMacToBytesRejectsShortMac() {
StringUtils.macToBytes("00:00:00:00:00");
}
@Test(expected = IllegalArgumentException.class)
public void testMacToBytesRejectsLongMac() {
StringUtils.macToBytes("00:00:00:00:00:00:00");
}
@Test(expected = IllegalArgumentException.class)
public void testMacToBytesRejectsInvalidCharacter() {
StringUtils.macToBytes("00:00:00:00:00:0g");
}
@Test(expected = IllegalArgumentException.class)
public void testMacToBytesRejectsInvalidFormat() {
StringUtils.macToBytes("0:000:00:00:00:00");
}
@Test
public void testMacToBytesUpperCase() {
byte[] expected = new byte[] {0x0A, 0x1B, 0x2C, 0x3D, 0x4E, 0x5F};
String mac = "0A:1B:2C:3D:4E:5F";
assertArrayEquals(expected, StringUtils.macToBytes(mac));
}
@Test
public void testMacToBytesLowerCase() {
byte[] expected = new byte[] {0x0A, 0x1B, 0x2C, 0x3D, 0x4E, 0x5F};
String mac = "0a:1b:2c:3d:4e:5f";
assertArrayEquals(expected, StringUtils.macToBytes(mac));
}
@Test(expected = IllegalArgumentException.class)
public void testMacToStringRejectsShortMac() {
StringUtils.macToString(new byte[5]);
}
@Test(expected = IllegalArgumentException.class)
public void testMacToStringRejectsLongMac() {
StringUtils.macToString(new byte[7]);
}
@Test
public void testMacToString() {
byte[] mac = new byte[] {0x0a, 0x1b, 0x2c, 0x3d, 0x4e, 0x5f};
String expected = "0A:1B:2C:3D:4E:5F";
assertEquals(expected, StringUtils.macToString(mac));
}
} }