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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,24 +1,34 @@
package org.briarproject.util;
import org.briarproject.api.nullsafety.NotNullByDefault;
import java.io.UnsupportedEncodingException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Collection;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import static java.nio.charset.CodingErrorAction.IGNORE;
import static java.util.regex.Pattern.CASE_INSENSITIVE;
@NotNullByDefault
public class StringUtils {
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[] {
'0', '1', '2', '3', '4', '5', '6', '7',
'8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
'0', '1', '2', '3', '4', '5', '6', '7',
'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;
}
@@ -61,7 +71,9 @@ public class StringUtils {
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) {
char[] hex = new char[bytes.length * 2];
for (int i = 0, j = 0; i < bytes.length; i++) {
@@ -71,12 +83,16 @@ public class StringUtils {
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) {
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) {
int len = hex.length();
if (len % 2 != 0)
@@ -107,4 +123,20 @@ public class StringUtils {
public static boolean utf8IsTooLong(String s, int 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();
}
}