Compare commits

..

9 Commits

Author SHA1 Message Date
MajorCrazed
7e9153bd81 remove force disable 2017-11-06 16:56:30 +01:00
MajorCrazed
15fa44c1b1 remove comment 2017-11-06 16:43:02 +01:00
MajorCrazed
7ffe1b8bae move force variable to BluetoothDisableEvent 2017-11-02 13:31:21 +01:00
MajorCrazed
bb8713ddcb enable bluetooth by default 2017-11-02 13:30:54 +01:00
MajorCrazed
1e9afd14db set the reason to enable or disable bluetooth 2017-11-01 17:36:36 +01:00
MajorCrazed
86ea6eae63 handle different reasons why bluetooth should be enabled 2017-11-01 17:35:11 +01:00
MajorCrazed
56ba639084 enum to set the reason why bluetooth is enabled 2017-11-01 17:33:12 +01:00
MajorCrazed
8c16c2107f disable bluetooth after adding contact 2017-10-29 23:31:21 +01:00
MajorCrazed
cefe2b09e0 add option to force enable or disable bluetooth adapter 2017-10-29 23:30:47 +01:00
383 changed files with 9739 additions and 11078 deletions

View File

@@ -6,18 +6,8 @@ cache:
- .gradle/caches - .gradle/caches
before_script: before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle - export GRADLE_USER_HOME=$PWD/.gradle
# Accept the license for the Android build tools - echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;23.0.3"
- echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;26.0.2"
# Download OpenJDK 6 so we can compile against its standard library
- JDK_FILE=openjdk-6-jre-headless_6b38-1.13.10-1~deb7u1_amd64.deb
- if [ ! -d openjdk ]
- then
- wget -q http://ftp.uk.debian.org/debian/pool/main/o/openjdk-6/$JDK_FILE
- dpkg-deb -x $JDK_FILE openjdk
- fi
- export JAVA_6_HOME=$PWD/openjdk/usr/lib/jvm/java-6-openjdk-amd64
test: test:
script: script:

View File

@@ -6,91 +6,99 @@ apply plugin: 'witness'
apply plugin: 'de.undercouch.download' apply plugin: 'de.undercouch.download'
android { android {
compileSdkVersion 27 compileSdkVersion 23
buildToolsVersion '26.0.2' buildToolsVersion '25.0.0'
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 22
versionCode 1700 versionCode 1611
versionName "0.17.0" versionName "0.16.11"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
} }
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_7
} }
} }
dependencies { dependencies {
implementation project(path: ':bramble-core', configuration: 'default') compile project(path: ':bramble-core', configuration: 'default')
implementation fileTree(dir: 'libs', include: '*.jar') compile fileTree(dir: 'libs', include: '*.jar')
provided 'javax.annotation:jsr250-api:1.0'
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
compileOnly 'javax.annotation:jsr250-api:1.0'
} }
dependencyVerification { def torBinaryDir = 'src/main/res/raw'
verify = [
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', task downloadTorGeoIp(type: Download) {
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3', src 'https://briarproject.org/build/geoip-2017-09-06.zip'
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b', dest "$torBinaryDir/geoip.zip"
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9', onlyIfNewer true
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128-runtime.jar:e357a0f1d573c2f702a273992b1b6cb661734f66311854efb3778a888515c5b5',
'org.jacoco:org.jacoco.agent:0.7.4.201502262128:org.jacoco.agent-0.7.4.201502262128.jar:47b4bec6df11a1118da3953da8b9fa1e7079d6fec857faa1a3cf912e53a6fd4e',
'org.jacoco:org.jacoco.ant:0.7.4.201502262128:org.jacoco.ant-0.7.4.201502262128.jar:013ce2a68ba57a3c59215ae0dec4df3498c078062a38c3b94c841fc14450f283',
'org.jacoco:org.jacoco.core:0.7.4.201502262128:org.jacoco.core-0.7.4.201502262128.jar:ec4c74554312fac5116350164786f91b35c9e082fa4ea598bfa42b5db05d7abb',
'org.jacoco:org.jacoco.report:0.7.4.201502262128:org.jacoco.report-0.7.4.201502262128.jar:7a3554c605e088e7e323b1084656243f0444fa353e2f2dee1f1a4204eb64ff09',
'org.ow2.asm:asm-debug-all:5.0.1:asm-debug-all-5.0.1.jar:4734de5b515a454b0096db6971fb068e5f70e6f10bbee2b3bd2fdfe5d978ed57',
]
} }
ext.torBinaryDir = 'src/main/res/raw' task downloadTorBinaryArm(type: Download) {
ext.torVersion = '0.2.9.12' src 'https://briarproject.org/build/tor-0.2.9.12-arm.zip'
ext.geoipVersion = '2017-09-06' dest "$torBinaryDir/tor_arm.zip"
ext.torDownloadUrl = 'https://briarproject.org/build/' onlyIfNewer true
def torBinaries = [
"tor_arm" : '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688',
"tor_arm_pie": '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283',
"tor_x86" : '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab',
"tor_x86_pie": '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288',
"geoip" : 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
]
def downloadBinary(name) {
return tasks.create("downloadBinary${name}", Download) {
src "${torDownloadUrl}${name}.zip"
.replace('tor_', "tor-${torVersion}-")
.replace('geoip', "geoip-${geoipVersion}")
.replaceAll('_', '-')
dest "${torBinaryDir}/${name}.zip"
onlyIfNewer true
}
} }
def verifyBinary(name, chksum) { task downloadTorBinaryArmPie(type: Download) {
return tasks.create([ src 'https://briarproject.org/build/tor-0.2.9.12-arm-pie.zip'
name : "verifyBinary${name}", dest "$torBinaryDir/tor_arm_pie.zip"
type : Verify, onlyIfNewer true
dependsOn: downloadBinary(name)]) { }
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256' task downloadTorBinaryX86(type: Download) {
checksum chksum src 'https://briarproject.org/build/tor-0.2.9.12-x86.zip'
} dest "$torBinaryDir/tor_x86.zip"
onlyIfNewer true
}
task downloadTorBinaryX86Pie(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.12-x86-pie.zip'
dest "$torBinaryDir/tor_x86_pie.zip"
onlyIfNewer true
}
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
src "$torBinaryDir/geoip.zip"
algorithm 'SHA-256'
checksum 'fe49d3adb86d3c512373101422a017dbb86c85a570524663f09dd8ce143a24f3'
}
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
src "$torBinaryDir/tor_arm.zip"
algorithm 'SHA-256'
checksum '8ed0b347ffed1d6a4d2fd14495118eb92be83e9cc06e057e15220dc288b31688'
}
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
src "$torBinaryDir/tor_arm_pie.zip"
algorithm 'SHA-256'
checksum '64403262511c29f462ca5e7c7621bfc3c944898364d1d5ad35a016bb8a034283'
}
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
src "$torBinaryDir/tor_x86.zip"
algorithm 'SHA-256'
checksum '61e014607a2079bcf1646289c67bff6372b1aded6e1d8d83d7791efda9a4d5ab'
}
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
src "$torBinaryDir/tor_x86_pie.zip"
algorithm 'SHA-256'
checksum '18fbc98356697dd0895836ab46d5c9877d1c539193464f7db1e82a65adaaf288'
} }
project.afterEvaluate { project.afterEvaluate {
torBinaries.every { key, value -> preBuild.dependsOn {
preBuild.dependsOn.add(verifyBinary(key, value)) [
'verifyTorGeoIp',
'verifyTorBinaryArm',
'verifyTorBinaryArmPie',
'verifyTorBinaryX86',
'verifyTorBinaryX86Pie'
]
} }
} }

View File

@@ -8,8 +8,6 @@
-dontwarn dagger.** -dontwarn dagger.**
-dontnote dagger.** -dontnote dagger.**
-keep class net.i2p.crypto.eddsa.** { *; }
-dontwarn sun.misc.Unsafe -dontwarn sun.misc.Unsafe
-dontnote com.google.common.** -dontnote com.google.common.**

View File

@@ -45,7 +45,7 @@ public class AndroidPluginModule {
backoffFactory); backoffFactory);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor, DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
backoffFactory, appContext); backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex = final Collection<DuplexPluginFactory> duplex =
Arrays.asList(bluetooth, tor, lan); Arrays.asList(bluetooth, tor, lan);
@NotNullByDefault @NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() { PluginConfig pluginConfig = new PluginConfig() {

View File

@@ -8,6 +8,7 @@ import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.widget.ArrayAdapter;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
@@ -19,6 +20,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementListener;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.Backoff; import org.briarproject.bramble.api.plugin.Backoff;
import org.briarproject.bramble.api.plugin.BluetoothEnableDisableReason;
import org.briarproject.bramble.api.plugin.PluginException; import org.briarproject.bramble.api.plugin.PluginException;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin; import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
@@ -34,6 +36,7 @@ import org.briarproject.bramble.util.StringUtils;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException; import java.io.IOException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.Map.Entry;
@@ -83,6 +86,8 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
private volatile boolean running = false; private volatile boolean running = false;
private volatile boolean wasEnabledByUs = false; private volatile boolean wasEnabledByUs = false;
private volatile ArrayList<BluetoothEnableDisableReason>
enableDisableReasons = new ArrayList<>();
private volatile BluetoothStateReceiver receiver = null; private volatile BluetoothStateReceiver receiver = null;
private volatile BluetoothServerSocket socket = null; private volatile BluetoothServerSocket socket = null;
@@ -124,7 +129,12 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
// with a message queue, so submit it to the AndroidExecutor // with a message queue, so submit it to the AndroidExecutor
try { try {
adapter = androidExecutor.runOnBackgroundThread( adapter = androidExecutor.runOnBackgroundThread(
BluetoothAdapter::getDefaultAdapter).get(); new Callable<BluetoothAdapter>() {
@Override
public BluetoothAdapter call() throws Exception {
return BluetoothAdapter.getDefaultAdapter();
}
}).get();
} catch (InterruptedException e) { } catch (InterruptedException e) {
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
LOG.warning("Interrupted while getting BluetoothAdapter"); LOG.warning("Interrupted while getting BluetoothAdapter");
@@ -148,8 +158,8 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
bind(); bind();
} else { } else {
// Enable Bluetooth if settings allow // Enable Bluetooth if settings allow
if (callback.getSettings().getBoolean(PREF_BT_ENABLE, false)) { if (callback.getSettings().getBoolean(PREF_BT_ENABLE, true)) {
enableAdapter(); enableAdapter(BluetoothEnableDisableReason.COMMUNICATION);
} else { } else {
LOG.info("Not enabling Bluetooth"); LOG.info("Not enabling Bluetooth");
} }
@@ -157,36 +167,40 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
} }
private void bind() { private void bind() {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
if (!isRunning()) return; @Override
String address = AndroidUtils.getBluetoothAddress(appContext, public void run() {
adapter); if (!isRunning()) return;
if (LOG.isLoggable(INFO)) String address = AndroidUtils.getBluetoothAddress(appContext,
LOG.info("Local address " + scrubMacAddress(address)); adapter);
if (!StringUtils.isNullOrEmpty(address)) { if (LOG.isLoggable(INFO))
// Advertise the Bluetooth address to contacts LOG.info("Local address " + scrubMacAddress(address));
TransportProperties p = new TransportProperties(); if (!StringUtils.isNullOrEmpty(address)) {
p.put(PROP_ADDRESS, address); // Advertise the Bluetooth address to contacts
callback.mergeLocalProperties(p); TransportProperties p = new TransportProperties();
p.put(PROP_ADDRESS, address);
callback.mergeLocalProperties(p);
}
// Bind a server socket to accept connections from contacts
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", getUuid());
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
return;
}
if (!isRunning()) {
tryToClose(ss);
return;
}
LOG.info("Socket bound");
socket = ss;
backoff.reset();
callback.transportEnabled();
acceptContactConnections();
} }
// Bind a server socket to accept connections from contacts
BluetoothServerSocket ss;
try {
ss = adapter.listenUsingInsecureRfcommWithServiceRecord(
"RFCOMM", getUuid());
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return;
}
if (!isRunning()) {
tryToClose(ss);
return;
}
LOG.info("Socket bound");
socket = ss;
backoff.reset();
callback.transportEnabled();
acceptContactConnections();
}); });
} }
@@ -236,11 +250,15 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
return new DroidtoothTransportConnection(this, s); return new DroidtoothTransportConnection(this, s);
} }
private void enableAdapter() { private void enableAdapter(BluetoothEnableDisableReason reason) {
if (adapter != null && !adapter.isEnabled()) { if (adapter != null && !adapter.isEnabled()) {
if (adapter.enable()) { if (adapter.enable()) {
LOG.info("Enabling Bluetooth"); LOG.info("Enabling Bluetooth");
wasEnabledByUs = true; wasEnabledByUs = true;
if(!enableDisableReasons.contains(reason)) {
enableDisableReasons.add(reason);
}
} else { } else {
LOG.info("Could not enable Bluetooth"); LOG.info("Could not enable Bluetooth");
} }
@@ -252,11 +270,22 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
running = false; running = false;
if (receiver != null) appContext.unregisterReceiver(receiver); if (receiver != null) appContext.unregisterReceiver(receiver);
tryToClose(socket); tryToClose(socket);
disableAdapter(); disableAdapter(true);
} }
private void disableAdapter() { private void disableAdapter(boolean force){
if (adapter != null && adapter.isEnabled() && wasEnabledByUs) { disableAdapter(null, force);
}
private void disableAdapter(BluetoothEnableDisableReason reason,
boolean force) {
if (adapter != null && adapter.isEnabled() && wasEnabledByUs
&& (enableDisableReasons.contains(reason) || force)) {
if(enableDisableReasons.contains(reason)){
enableDisableReasons.remove(reason);
}
if (adapter.disable()) LOG.info("Disabling Bluetooth"); if (adapter.disable()) LOG.info("Disabling Bluetooth");
else LOG.info("Could not disable Bluetooth"); else LOG.info("Could not disable Bluetooth");
} }
@@ -285,18 +314,21 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
Map<ContactId, TransportProperties> remote = Map<ContactId, TransportProperties> remote =
callback.getRemoteProperties(); callback.getRemoteProperties();
for (Entry<ContactId, TransportProperties> e : remote.entrySet()) { for (Entry<ContactId, TransportProperties> e : remote.entrySet()) {
ContactId c = e.getKey(); final ContactId c = e.getKey();
if (connected.contains(c)) continue; if (connected.contains(c)) continue;
String address = e.getValue().get(PROP_ADDRESS); final String address = e.getValue().get(PROP_ADDRESS);
if (StringUtils.isNullOrEmpty(address)) continue; if (StringUtils.isNullOrEmpty(address)) continue;
String uuid = e.getValue().get(PROP_UUID); final String uuid = e.getValue().get(PROP_UUID);
if (StringUtils.isNullOrEmpty(uuid)) continue; if (StringUtils.isNullOrEmpty(uuid)) continue;
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
if (!running) return; @Override
BluetoothSocket s = connect(address, uuid); public void run() {
if (s != null) { if (!running) return;
backoff.reset(); BluetoothSocket s = connect(address, uuid);
callback.outgoingConnectionCreated(c, wrapSocket(s)); if (s != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, wrapSocket(s));
}
} }
}); });
} }
@@ -419,18 +451,30 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof EnableBluetoothEvent) { if (e instanceof EnableBluetoothEvent) {
enableAdapterAsync(); EnableBluetoothEvent enable = (EnableBluetoothEvent) e;
enableAdapterAsync(enable.getReason());
} else if (e instanceof DisableBluetoothEvent) { } else if (e instanceof DisableBluetoothEvent) {
disableAdapterAsync(); DisableBluetoothEvent disable = (DisableBluetoothEvent) e;
disableAdapterAsync(disable.getReason());
} }
} }
private void enableAdapterAsync() { private void enableAdapterAsync(final BluetoothEnableDisableReason reason) {
ioExecutor.execute(this::enableAdapter); ioExecutor.execute(new Runnable() {
@Override
public void run() {
enableAdapter(reason);
}
});
} }
private void disableAdapterAsync() { private void disableAdapterAsync(final BluetoothEnableDisableReason reason) {
ioExecutor.execute(this::disableAdapter); ioExecutor.execute(new Runnable() {
@Override
public void run() {
disableAdapter(reason, false);
}
});
} }
private class BluetoothStateReceiver extends BroadcastReceiver { private class BluetoothStateReceiver extends BroadcastReceiver {
@@ -468,13 +512,16 @@ class DroidtoothPlugin implements DuplexPlugin, EventListener {
@Override @Override
public Callable<KeyAgreementConnection> listen() { public Callable<KeyAgreementConnection> listen() {
return () -> { return new Callable<KeyAgreementConnection>() {
BluetoothSocket s = ss.accept(); @Override
if (LOG.isLoggable(INFO)) public KeyAgreementConnection call() throws IOException {
LOG.info(ID.getString() + ": Incoming connection"); BluetoothSocket s = ss.accept();
return new KeyAgreementConnection( if (LOG.isLoggable(INFO))
new DroidtoothTransportConnection( LOG.info(ID.getString() + ": Incoming connection");
DroidtoothPlugin.this, s), ID); return new KeyAgreementConnection(
new DroidtoothTransportConnection(
DroidtoothPlugin.this, s), ID);
}
}; };
} }

View File

@@ -370,45 +370,57 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void sendDevReports() { private void sendDevReports() {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
// TODO: Trigger this with a TransportEnabledEvent @Override
File reportDir = AndroidUtils.getReportDir(appContext); public void run() {
reporter.sendReports(reportDir); // TODO: Trigger this with a TransportEnabledEvent
File reportDir = AndroidUtils.getReportDir(appContext);
reporter.sendReports(reportDir);
}
}); });
} }
private void bind() { private void bind() {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
// If there's already a port number stored in config, reuse it @Override
String portString = callback.getSettings().get(PREF_TOR_PORT); public void run() {
int port; // If there's already a port number stored in config, reuse it
if (StringUtils.isNullOrEmpty(portString)) port = 0; String portString = callback.getSettings().get(PREF_TOR_PORT);
else port = Integer.parseInt(portString); int port;
// Bind a server socket to receive connections from Tor if (StringUtils.isNullOrEmpty(portString)) port = 0;
ServerSocket ss = null; else port = Integer.parseInt(portString);
try { // Bind a server socket to receive connections from Tor
ss = new ServerSocket(); ServerSocket ss = null;
ss.bind(new InetSocketAddress("127.0.0.1", port)); try {
} catch (IOException e) { ss = new ServerSocket();
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); ss.bind(new InetSocketAddress("127.0.0.1", port));
tryToClose(ss); } catch (IOException e) {
return; if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
tryToClose(ss);
return;
}
if (!running) {
tryToClose(ss);
return;
}
socket = ss;
// Store the port number
final String localPort = String.valueOf(ss.getLocalPort());
Settings s = new Settings();
s.put(PREF_TOR_PORT, localPort);
callback.mergeSettings(s);
// Create a hidden service if necessary
ioExecutor.execute(new Runnable() {
@Override
public void run() {
publishHiddenService(localPort);
}
});
backoff.reset();
// Accept incoming hidden service connections from Tor
acceptContactConnections(ss);
} }
if (!running) {
tryToClose(ss);
return;
}
socket = ss;
// Store the port number
String localPort = String.valueOf(ss.getLocalPort());
Settings s = new Settings();
s.put(PREF_TOR_PORT, localPort);
callback.mergeSettings(s);
// Create a hidden service if necessary
ioExecutor.execute(() -> publishHiddenService(localPort));
backoff.reset();
// Accept incoming hidden service connections from Tor
acceptContactConnections(ss);
}); });
} }
@@ -536,13 +548,17 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
} }
private void connectAndCallBack(ContactId c, TransportProperties p) { private void connectAndCallBack(final ContactId c,
ioExecutor.execute(() -> { final TransportProperties p) {
if (!isRunning()) return; ioExecutor.execute(new Runnable() {
DuplexTransportConnection d = createConnection(p); @Override
if (d != null) { public void run() {
backoff.reset(); if (!isRunning()) return;
callback.outgoingConnectionCreated(c, d); DuplexTransportConnection d = createConnection(p);
if (d != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, d);
}
} }
}); });
} }
@@ -675,43 +691,48 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
} }
private void updateConnectionStatus() { private void updateConnectionStatus() {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
if (!running) return; @Override
public void run() {
if (!running) return;
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE); Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
ConnectivityManager cm = (ConnectivityManager) o; ConnectivityManager cm = (ConnectivityManager) o;
NetworkInfo net = cm.getActiveNetworkInfo(); NetworkInfo net = cm.getActiveNetworkInfo();
boolean online = net != null && net.isConnected(); boolean online = net != null && net.isConnected();
boolean wifi = online && net.getType() == TYPE_WIFI; boolean wifi = online && net.getType() == TYPE_WIFI;
String country = locationUtils.getCurrentCountry(); String country = locationUtils.getCurrentCountry();
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked( boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
country); country);
Settings s = callback.getSettings(); Settings s = callback.getSettings();
int network = s.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_ALWAYS); int network = s.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_ALWAYS);
if (LOG.isLoggable(INFO)) { if (LOG.isLoggable(INFO)) {
LOG.info("Online: " + online + ", wifi: " + wifi); LOG.info("Online: " + online + ", wifi: " + wifi);
if ("".equals(country)) LOG.info("Country code unknown"); if ("".equals(country)) LOG.info("Country code unknown");
else LOG.info("Country code: " + country); else LOG.info("Country code: " + country);
} }
try { try {
if (!online) { if (!online) {
LOG.info("Disabling network, device is offline"); LOG.info("Disabling network, device is offline");
enableNetwork(false); enableNetwork(false);
} else if (blocked) { } else if (blocked) {
LOG.info("Disabling network, country is blocked"); LOG.info("Disabling network, country is blocked");
enableNetwork(false); enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_NEVER } else if (network == PREF_TOR_NETWORK_NEVER
|| (network == PREF_TOR_NETWORK_WIFI && !wifi)) { || (network == PREF_TOR_NETWORK_WIFI && !wifi)) {
LOG.info("Disabling network due to data setting"); LOG.info("Disabling network due to data setting");
enableNetwork(false); enableNetwork(false);
} else { } else {
LOG.info("Enabling network"); LOG.info("Enabling network");
enableNetwork(true); enableNetwork(true);
}
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
} }
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
}); });
} }

View File

@@ -27,11 +27,14 @@ class AndroidExecutorImpl implements AndroidExecutor {
@Inject @Inject
AndroidExecutorImpl(Application app) { AndroidExecutorImpl(Application app) {
uiHandler = new Handler(app.getApplicationContext().getMainLooper()); uiHandler = new Handler(app.getApplicationContext().getMainLooper());
loop = () -> { loop = new Runnable() {
Looper.prepare(); @Override
backgroundHandler = new Handler(); public void run() {
startLatch.countDown(); Looper.prepare();
Looper.loop(); backgroundHandler = new Handler();
startLatch.countDown();
Looper.loop();
}
}; };
} }

View File

@@ -1,39 +1,30 @@
apply plugin: 'java-library' apply plugin: 'java'
sourceCompatibility = 1.8 sourceCompatibility = 1.6
targetCompatibility = 1.8 targetCompatibility = 1.6
apply plugin: 'witness' apply plugin: 'witness'
dependencies { dependencies {
implementation "com.google.dagger:dagger:2.0.2" compile "com.google.dagger:dagger:2.0.2"
implementation 'com.google.code.findbugs:jsr305:3.0.2' compile 'com.google.dagger:dagger-compiler:2.0.2'
compile 'com.google.code.findbugs:jsr305:3.0.2'
testImplementation 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testImplementation "org.jmock:jmock:2.8.2" testCompile "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:2.8.2" testCompile "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2" testCompile "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.hamcrest:hamcrest-library:1.3" testCompile "org.hamcrest:hamcrest-library:1.3"
testImplementation "org.hamcrest:hamcrest-core:1.3" testCompile "org.hamcrest:hamcrest-core:1.3"
} }
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9', 'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d', 'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
] ]
} }
@@ -48,8 +39,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts { artifacts {
testOutput jarTest testOutput jarTest
} }
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
tasks.withType(JavaCompile) {
useJava6StandardLibrary(it)
}

View File

@@ -23,7 +23,7 @@ public class BdfMessageContext {
} }
public BdfMessageContext(BdfDictionary dictionary) { public BdfMessageContext(BdfDictionary dictionary) {
this(dictionary, Collections.emptyList()); this(dictionary, Collections.<MessageId>emptyList());
} }
public BdfDictionary getDictionary() { public BdfDictionary getDictionary() {

View File

@@ -12,19 +12,18 @@ public interface ContactGroupFactory {
/** /**
* Creates a group that is not shared with any contacts. * Creates a group that is not shared with any contacts.
*/ */
Group createLocalGroup(ClientId clientId, int clientVersion); Group createLocalGroup(ClientId clientId);
/** /**
* Creates a group for the given client to share with the given contact. * Creates a group for the given client to share with the given contact.
*/ */
Group createContactGroup(ClientId clientId, int clientVersion, Group createContactGroup(ClientId clientId, Contact contact);
Contact contact);
/** /**
* Creates a group for the given client to share between the given authors * Creates a group for the given client to share between the given authors
* identified by their AuthorIds. * identified by their AuthorIds.
*/ */
Group createContactGroup(ClientId clientId, int clientVersion, Group createContactGroup(ClientId clientId, AuthorId authorId1,
AuthorId authorId1, AuthorId authorId2); AuthorId authorId2);
} }

View File

@@ -12,32 +12,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault @NotNullByDefault
public interface ContactExchangeTask { public interface ContactExchangeTask {
/**
* The current version of the contact exchange protocol
*/
int PROTOCOL_VERSION = 0;
/**
* Label for deriving Alice's header key from the master secret.
*/
String ALICE_KEY_LABEL =
"org.briarproject.bramble.contact/ALICE_HEADER_KEY";
/**
* Label for deriving Bob's header key from the master secret.
*/
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
/**
* Label for deriving Alice's key binding nonce from the master secret.
*/
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
/**
* Label for deriving Bob's key binding nonce from the master secret.
*/
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
/** /**
* Exchanges contact information with a remote peer. * Exchanges contact information with a remote peer.
*/ */

View File

@@ -1,5 +1,8 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.SecureRandom; import java.security.SecureRandom;
@@ -17,98 +20,156 @@ public interface CryptoComponent {
KeyParser getSignatureKeyParser(); KeyParser getSignatureKeyParser();
KeyPair generateEdKeyPair();
KeyParser getEdKeyParser();
KeyParser getMessageKeyParser(); KeyParser getMessageKeyParser();
/** /**
* Derives another secret key from the given secret key. * Derives a stream header key from the given master secret.
* * @param alice whether the key is for use by Alice or Bob.
* @param label a namespaced label indicating the purpose of the derived
* key, to prevent it from being repurposed or colliding with a key derived
* for another purpose
*/ */
SecretKey deriveKey(String label, SecretKey k, byte[]... inputs); SecretKey deriveHeaderKey(SecretKey master, boolean alice);
/**
* Derives a message authentication code key from the given master secret.
* @param alice whether the key is for use by Alice or Bob.
*/
SecretKey deriveMacKey(SecretKey master, boolean alice);
/**
* Derives a nonce from the given master secret for one of the parties to
* sign.
* @param alice whether the nonce is for use by Alice or Bob.
*/
byte[] deriveSignatureNonce(SecretKey master, boolean alice);
/**
* Derives a commitment to the provided public key.
* <p/>
* Part of BQP.
*
* @param publicKey the public key
* @return the commitment to the provided public key.
*/
byte[] deriveKeyCommitment(byte[] publicKey);
/** /**
* Derives a common shared secret from two public keys and one of the * Derives a common shared secret from two public keys and one of the
* corresponding private keys. * corresponding private keys.
* <p/>
* Part of BQP.
* *
* @param label a namespaced label indicating the purpose of this shared * @param theirPublicKey the ephemeral public key of the remote party
* secret, to prevent it from being repurposed or colliding with a shared * @param ourKeyPair our ephemeral keypair
* secret derived for another purpose * @param alice true if ourKeyPair belongs to Alice
* @param theirPublicKey the public key of the remote party
* @param ourKeyPair the key pair of the local party
* @return the shared secret * @return the shared secret
* @throws GeneralSecurityException
*/ */
SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey, SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
KeyPair ourKeyPair, byte[]... inputs) boolean alice) throws GeneralSecurityException;
throws GeneralSecurityException;
/** /**
* Signs the given byte[] with the given ECDSA private key. * Derives the content of a confirmation record.
* <p/>
* Part of BQP.
* *
* @param label a namespaced label indicating the purpose of this * @param sharedSecret the common shared secret
* signature, to prevent it from being repurposed or colliding with a * @param theirPayload the commit payload from the remote party
* signature created for another purpose * @param ourPayload the commit payload we sent
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @param aliceRecord true if the confirmation record is for use by Alice
* @return the confirmation record
*/
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload,
byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice, boolean aliceRecord);
/**
* Derives a master secret from the given shared secret.
* <p/>
* Part of BQP.
*
* @param sharedSecret the common shared secret
* @return the master secret
*/
SecretKey deriveMasterSecret(SecretKey sharedSecret);
/**
* Derives a master secret from two public keys and one of the corresponding
* private keys.
* <p/>
* This is a helper method that calls
* deriveMasterSecret(deriveSharedSecret(theirPublicKey, ourKeyPair, alice))
*
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral keypair
* @param alice true if ourKeyPair belongs to Alice
* @return the shared secret
* @throws GeneralSecurityException
*/
SecretKey deriveMasterSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) throws GeneralSecurityException;
/**
* Derives initial transport keys for the given transport in the given
* rotation period from the given master secret.
* @param alice whether the keys are for use by Alice or Bob.
*/
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
long rotationPeriod, boolean alice);
/**
* Rotates the given transport keys to the given rotation period. If the
* keys are for a future rotation period they are not rotated.
*/
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/** Encodes the pseudo-random tag that is used to recognise a stream. */
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber);
/**
* Signs the given byte[] with the given PrivateKey.
*
* @param label A label specific to this signature
* to ensure that the signature cannot be repurposed
*/ */
byte[] sign(String label, byte[] toSign, byte[] privateKey) byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException; throws GeneralSecurityException;
/** /**
* Signs the given byte[] with the given Ed25519 private key. * Verifies that the given signature is valid for the signedData
* and the given publicKey.
* *
* @param label A label specific to this signature * @param label A label that was specific to this signature
* to ensure that the signature cannot be repurposed * to ensure that the signature cannot be repurposed
*/
byte[] signEd(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException;
/**
* Verifies that the given signature is valid for the signed data
* and the given ECDSA public key.
*
* @param label a namespaced label indicating the purpose of this
* signature, to prevent it from being repurposed or colliding with a
* signature created for another purpose
* @return true if the signature was valid, false otherwise. * @return true if the signature was valid, false otherwise.
*/ */
boolean verify(String label, byte[] signedData, byte[] publicKey, boolean verify(String label, byte[] signedData, byte[] publicKey,
byte[] signature) throws GeneralSecurityException; byte[] signature) throws GeneralSecurityException;
/**
* Verifies that the given signature is valid for the signed data
* and the given Ed25519 public key.
*
* @param label A label that was specific to this signature
* to ensure that the signature cannot be repurposed
* @return true if the signature was valid, false otherwise.
*/
boolean verifyEd(String label, byte[] signedData, byte[] publicKey,
byte[] signature) throws GeneralSecurityException;
/** /**
* Returns the hash of the given inputs. The inputs are unambiguously * Returns the hash of the given inputs. The inputs are unambiguously
* combined by prefixing each input with its length. * combined by prefixing each input with its length.
* *
* @param label a namespaced label indicating the purpose of this hash, to * @param label A label specific to this hash to ensure that hashes
* prevent it from being repurposed or colliding with a hash created for * calculated for distinct purposes don't collide.
* another purpose
*/ */
byte[] hash(String label, byte[]... inputs); byte[] hash(String label, byte[]... inputs);
/**
* Returns the length of hashes produced by
* the {@link CryptoComponent#hash(String, byte[]...)} method.
*/
int getHashLength();
/** /**
* Returns a message authentication code with the given key over the * Returns a message authentication code with the given key over the
* given inputs. The inputs are unambiguously combined by prefixing each * given inputs. The inputs are unambiguously combined by prefixing each
* input with its length. * input with its length.
*
* @param label a namespaced label indicating the purpose of this MAC, to
* prevent it from being repurposed or colliding with a MAC created for
* another purpose
*/ */
byte[] mac(String label, SecretKey macKey, byte[]... inputs); byte[] mac(SecretKey macKey, byte[]... inputs);
/** /**
* Encrypts and authenticates the given plaintext so it can be written to * Encrypts and authenticates the given plaintext so it can be written to

View File

@@ -1,50 +0,0 @@
package org.briarproject.bramble.api.crypto;
/**
* Crypto operations for the key agreement protocol - see
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md
*/
public interface KeyAgreementCrypto {
/**
* Hash label for public key commitment.
*/
String COMMIT_LABEL = "org.briarproject.bramble.keyagreement/COMMIT";
/**
* Key derivation label for confirmation record.
*/
String CONFIRMATION_KEY_LABEL =
"org.briarproject.bramble.keyagreement/CONFIRMATION_KEY";
/**
* MAC label for confirmation record.
*/
String CONFIRMATION_MAC_LABEL =
"org.briarproject.bramble.keyagreement/CONFIRMATION_MAC";
/**
* Derives a commitment to the provided public key.
*
* @param publicKey the public key
* @return the commitment to the provided public key.
*/
byte[] deriveKeyCommitment(PublicKey publicKey);
/**
* Derives the content of a confirmation record.
*
* @param sharedSecret the common shared secret
* @param theirPayload the key exchange payload of the remote party
* @param ourPayload the key exchange payload of the local party
* @param theirPublicKey the ephemeral public key of the remote party
* @param ourKeyPair our ephemeral key pair of the local party
* @param alice true if the local party is Alice
* @param aliceRecord true if the confirmation record is for use by Alice
* @return the confirmation record
*/
byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload,
PublicKey theirPublicKey, KeyPair ourKeyPair,
boolean alice, boolean aliceRecord);
}

View File

@@ -1,32 +0,0 @@
package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.TransportKeys;
/**
* Crypto operations for the transport security protocol - see
* https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md
*/
public interface TransportCrypto {
/**
* Derives initial transport keys for the given transport in the given
* rotation period from the given master secret.
*
* @param alice whether the keys are for use by Alice or Bob.
*/
TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
long rotationPeriod, boolean alice);
/**
* Rotates the given transport keys to the given rotation period. If the
* keys are for the given period or any later period they are not rotated.
*/
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/**
* Encodes the pseudo-random tag that is used to recognise a stream.
*/
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber);
}

View File

@@ -4,14 +4,11 @@ import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry; import java.util.concurrent.ConcurrentSkipListMap;
import java.util.TreeMap;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe public class BdfDictionary extends ConcurrentSkipListMap<String, Object> {
public class BdfDictionary extends TreeMap<String, Object> {
public static final Object NULL_VALUE = new Object(); public static final Object NULL_VALUE = new Object();

View File

@@ -3,17 +3,15 @@ package org.briarproject.bramble.api.data;
import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Vector;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE; import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
@NotThreadSafe public class BdfList extends Vector<Object> {
public class BdfList extends ArrayList<Object> {
/** /**
* Factory method for constructing lists inline. * Factory method for constructing lists inline.

View File

@@ -122,9 +122,8 @@ public interface DatabaseComponent {
throws DbException; throws DbException;
/** /**
* Deletes the message with the given ID. Unlike * Deletes the message with the given ID. The message ID and any other
* {@link #removeMessage(Transaction, MessageId)}, the message ID and any * associated data are not deleted.
* other associated data are not deleted.
*/ */
void deleteMessage(Transaction txn, MessageId m) throws DbException; void deleteMessage(Transaction txn, MessageId m) throws DbException;
@@ -453,11 +452,6 @@ public interface DatabaseComponent {
*/ */
void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException; void removeLocalAuthor(Transaction txn, AuthorId a) throws DbException;
/**
* Removes a message (and all associated state) from the database.
*/
void removeMessage(Transaction txn, MessageId m) throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */

View File

@@ -1,11 +1,11 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
import java.util.TreeMap; import java.util.Hashtable;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.ThreadSafe;
@NotThreadSafe @ThreadSafe
public class Metadata extends TreeMap<String, byte[]> { public class Metadata extends Hashtable<String, byte[]> {
/** /**
* Special value to indicate that a key is being removed. * Special value to indicate that a key is being removed.

View File

@@ -45,7 +45,7 @@ public class Transaction {
* committed. * committed.
*/ */
public void attach(Event e) { public void attach(Event e) {
if (events == null) events = new ArrayList<>(); if (events == null) events = new ArrayList<Event>();
events.add(e); events.add(e);
} }

View File

@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
/** /**
* Label for hashing authors to calculate their identities. * Label for hashing authors to calculate their identities.
*/ */
public static final String LABEL = "org.briarproject.bramble/AUTHOR_ID"; public static final String LABEL = "org.briarproject.bramble.AUTHOR_ID";
public AuthorId(byte[] id) { public AuthorId(byte[] id) {
super(id); super(id);

View File

@@ -5,7 +5,7 @@ public interface KeyAgreementConstants {
/** /**
* The current version of the BQP protocol. * The current version of the BQP protocol.
*/ */
byte PROTOCOL_VERSION = 3; byte PROTOCOL_VERSION = 2;
/** /**
* The length of the record header in bytes. * The length of the record header in bytes.
@@ -22,10 +22,7 @@ public interface KeyAgreementConstants {
*/ */
int COMMIT_LENGTH = 16; int COMMIT_LENGTH = 16;
/** long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
* The connection timeout in milliseconds.
*/
long CONNECTION_TIMEOUT = 20 * 1000;
/** /**
* The transport identifier for Bluetooth. * The transport identifier for Bluetooth.
@@ -36,16 +33,4 @@ public interface KeyAgreementConstants {
* The transport identifier for LAN. * The transport identifier for LAN.
*/ */
int TRANSPORT_ID_LAN = 1; int TRANSPORT_ID_LAN = 1;
/**
* Label for deriving the shared secret.
*/
String SHARED_SECRET_LABEL =
"org.briarproject.bramble.keyagreement/SHARED_SECRET";
/**
* Label for deriving the master secret.
*/
String MASTER_SECRET_LABEL =
"org.briarproject.bramble.keyagreement/MASTER_SECRET";
} }

View File

@@ -0,0 +1,15 @@
package org.briarproject.bramble.api.keyagreement;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
/**
* Manages tasks for conducting key agreements with remote peers.
*/
@NotNullByDefault
public interface KeyAgreementTaskFactory {
/**
* Gets the current key agreement task.
*/
KeyAgreementTask createTask();
}

View File

@@ -0,0 +1,6 @@
package org.briarproject.bramble.api.plugin;
public enum BluetoothEnableDisableReason {
COMMUNICATION,
ADD_CONTACT
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.plugin.BluetoothEnableDisableReason;
abstract class BluetoothEvent extends Event {
private BluetoothEnableDisableReason selectedReason;
BluetoothEvent(BluetoothEnableDisableReason reason){
selectedReason = reason;
}
public BluetoothEnableDisableReason getReason(){
return selectedReason;
}
}

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.api.plugin.event; package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothEnableDisableReason;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@@ -11,5 +11,8 @@ import javax.annotation.concurrent.Immutable;
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class DisableBluetoothEvent extends Event { public class DisableBluetoothEvent extends BluetoothEvent {
public DisableBluetoothEvent(BluetoothEnableDisableReason reason) {
super(reason);
}
} }

View File

@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.plugin.event;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothEnableDisableReason;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
@@ -10,5 +11,8 @@ import javax.annotation.concurrent.Immutable;
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class EnableBluetoothEvent extends Event { public class EnableBluetoothEvent extends BluetoothEvent {
public EnableBluetoothEvent(BluetoothEnableDisableReason reason){
super(reason);
}
} }

View File

@@ -17,11 +17,6 @@ public interface TransportPropertyManager {
*/ */
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties"); ClientId CLIENT_ID = new ClientId("org.briarproject.briar.properties");
/**
* The current version of the transport property client.
*/
int CLIENT_VERSION = 0;
/** /**
* Stores the given properties received while adding a contact - they will * Stores the given properties received while adding a contact - they will
* be superseded by any properties synced from the contact. * be superseded by any properties synced from the contact.
@@ -38,7 +33,7 @@ public interface TransportPropertyManager {
/** /**
* Returns the local transport properties for all transports. * Returns the local transport properties for all transports.
* <br/> * <br/>
* TODO: Transaction can be read-only when code is simplified * Read-Only
*/ */
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn) Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
throws DbException; throws DbException;

View File

@@ -36,8 +36,4 @@ public class ClientId implements Comparable<ClientId> {
return id.hashCode(); return id.hashCode();
} }
@Override
public String toString() {
return id;
}
} }

View File

@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
public interface GroupFactory { public interface GroupFactory {
/** /**
* Creates a group with the given client ID, client version and descriptor. * Creates a group with the given client ID and descriptor.
*/ */
Group createGroup(ClientId c, int clientVersion, byte[] descriptor); Group createGroup(ClientId c, byte[] descriptor);
} }

View File

@@ -15,7 +15,7 @@ public class GroupId extends UniqueId {
/** /**
* Label for hashing groups to calculate their identifiers. * Label for hashing groups to calculate their identifiers.
*/ */
public static final String LABEL = "org.briarproject.bramble/GROUP_ID"; public static final String LABEL = "org.briarproject.bramble.GROUP_ID";
public GroupId(byte[] id) { public GroupId(byte[] id) {
super(id); super(id);

View File

@@ -22,7 +22,7 @@ public class MessageContext {
} }
public MessageContext(Metadata metadata) { public MessageContext(Metadata metadata) {
this(metadata, Collections.emptyList()); this(metadata, Collections.<MessageId>emptyList());
} }
public Metadata getMetadata() { public Metadata getMetadata() {

View File

@@ -16,7 +16,7 @@ public class MessageId extends UniqueId {
/** /**
* Label for hashing messages to calculate their identifiers. * Label for hashing messages to calculate their identifiers.
*/ */
public static final String LABEL = "org.briarproject.bramble/MESSAGE_ID"; public static final String LABEL = "org.briarproject.bramble.MESSAGE_ID";
public MessageId(byte[] id) { public MessageId(byte[] id) {
super(id); super(id);

View File

@@ -7,7 +7,7 @@ public interface TransportConstants {
/** /**
* The current version of the transport protocol. * The current version of the transport protocol.
*/ */
int PROTOCOL_VERSION = 4; int PROTOCOL_VERSION = 3;
/** /**
* The length of the pseudo-random tag in bytes. * The length of the pseudo-random tag in bytes.
@@ -80,32 +80,4 @@ public interface TransportConstants {
* The size of the reordering window. * The size of the reordering window.
*/ */
int REORDERING_WINDOW_SIZE = 32; int REORDERING_WINDOW_SIZE = 32;
/**
* Label for deriving Alice's initial tag key from the master secret.
*/
String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY";
/**
* Label for deriving Bob's initial tag key from the master secret.
*/
String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY";
/**
* Label for deriving Alice's initial header key from the master secret.
*/
String ALICE_HEADER_LABEL =
"org.briarproject.bramble.transport/ALICE_HEADER_KEY";
/**
* Label for deriving Bob's initial header key from the master secret.
*/
String BOB_HEADER_LABEL =
"org.briarproject.bramble.transport/BOB_HEADER_KEY";
/**
* Label for deriving the next period's key in key rotation.
*/
String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE";
} }

View File

@@ -3,7 +3,8 @@ package org.briarproject.bramble.test;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.After; import org.junit.After;
public abstract class BrambleMockTestCase extends BrambleTestCase { public abstract class BrambleMockTestCase extends
BrambleTestCase {
protected final Mockery context = new Mockery(); protected final Mockery context = new Mockery();

View File

@@ -8,9 +8,12 @@ public abstract class BrambleTestCase {
public BrambleTestCase() { public BrambleTestCase() {
// Ensure exceptions thrown on worker threads cause tests to fail // Ensure exceptions thrown on worker threads cause tests to fail
UncaughtExceptionHandler fail = (thread, throwable) -> { UncaughtExceptionHandler fail = new UncaughtExceptionHandler() {
throwable.printStackTrace(); @Override
fail(); public void uncaughtException(Thread thread, Throwable throwable) {
throwable.printStackTrace();
fail();
}
}; };
Thread.setDefaultUncaughtExceptionHandler(fail); Thread.setDefaultUncaughtExceptionHandler(fail);
} }

View File

@@ -1,60 +1,28 @@
apply plugin: 'java-library' plugins {
sourceCompatibility = 1.8 id 'java'
targetCompatibility = 1.8 id 'net.ltgt.apt' version '0.9'
id 'idea'
}
sourceCompatibility = 1.6
targetCompatibility = 1.6
apply plugin: 'net.ltgt.apt'
apply plugin: 'idea'
apply plugin: 'witness' apply plugin: 'witness'
dependencies { dependencies {
implementation project(path: ':bramble-api', configuration: 'default') compile project(path: ':bramble-api', configuration: 'default')
implementation 'com.madgag.spongycastle:core:1.58.0.0' compile 'com.madgag.spongycastle:core:1.58.0.0'
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6 compile 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
implementation 'org.bitlet:weupnp:0.1.4' compile 'org.bitlet:weupnp:0.1.4'
implementation 'net.i2p.crypto:eddsa:0.2.0'
apt 'com.google.dagger:dagger-compiler:2.0.2' testCompile project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.hamcrest:hamcrest-library:1.3"
testImplementation "org.hamcrest:hamcrest-core:1.3"
testImplementation "org.whispersystems:curve25519-java:0.4.1"
testApt 'com.google.dagger:dagger-compiler:2.0.2'
} }
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'com.madgag.spongycastle:core:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3', 'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'com.google.dagger:dagger-producers:2.0-beta:dagger-producers-2.0-beta.jar:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.guava:guava:18.0:guava-18.0.jar:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
'com.h2database:h2:1.4.192:h2-1.4.192.jar:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'com.madgag.spongycastle:core:1.58.0.0:core-1.58.0.0.jar:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.whispersystems:curve25519-java:0.4.1:curve25519-java-0.4.1.jar:7dd659d8822c06c3aea1a47f18fac9e5761e29cab8100030b877db445005f03e',
] ]
} }
@@ -69,8 +37,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts { artifacts {
testOutput jarTest testOutput jarTest
} }
// If a Java 6 JRE is available, check we're not using any Java 7 or 8 APIs
tasks.withType(JavaCompile) {
useJava6StandardLibrary(it)
}

View File

@@ -24,7 +24,7 @@ public class PoliteExecutor implements Executor {
private final Object lock = new Object(); private final Object lock = new Object();
@GuardedBy("lock") @GuardedBy("lock")
private final Queue<Runnable> queue = new LinkedList<>(); private final Queue<Runnable> queue = new LinkedList<Runnable>();
private final Executor delegate; private final Executor delegate;
private final int maxConcurrentTasks; private final int maxConcurrentTasks;
private final Logger log; private final Logger log;
@@ -48,17 +48,20 @@ public class PoliteExecutor implements Executor {
} }
@Override @Override
public void execute(Runnable r) { public void execute(final Runnable r) {
long submitted = System.currentTimeMillis(); final long submitted = System.currentTimeMillis();
Runnable wrapped = () -> { Runnable wrapped = new Runnable() {
if (log.isLoggable(LOG_LEVEL)) { @Override
long queued = System.currentTimeMillis() - submitted; public void run() {
log.log(LOG_LEVEL, "Queue time " + queued + " ms"); if (log.isLoggable(LOG_LEVEL)) {
} long queued = System.currentTimeMillis() - submitted;
try { log.log(LOG_LEVEL, "Queue time " + queued + " ms");
r.run(); }
} finally { try {
scheduleNext(); r.run();
} finally {
scheduleNext();
}
} }
}; };
synchronized (lock) { synchronized (lock) {

View File

@@ -28,16 +28,19 @@ public class TimeLoggingExecutor extends ThreadPoolExecutor {
} }
@Override @Override
public void execute(Runnable r) { public void execute(final Runnable r) {
if (log.isLoggable(LOG_LEVEL)) { if (log.isLoggable(LOG_LEVEL)) {
long submitted = System.currentTimeMillis(); final long submitted = System.currentTimeMillis();
super.execute(() -> { super.execute(new Runnable() {
long started = System.currentTimeMillis(); @Override
long queued = started - submitted; public void run() {
log.log(LOG_LEVEL, "Queue time " + queued + " ms"); long started = System.currentTimeMillis();
r.run(); long queued = started - submitted;
long executing = System.currentTimeMillis() - started; log.log(LOG_LEVEL, "Queue time " + queued + " ms");
log.log(LOG_LEVEL, "Execution time " + executing + " ms"); r.run();
long executing = System.currentTimeMillis() - started;
log.log(LOG_LEVEL, "Execution time " + executing + " ms");
}
}); });
} else { } else {
super.execute(r); super.execute(r);

View File

@@ -201,7 +201,8 @@ class ClientHelperImpl implements ClientHelper {
public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary( public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
Transaction txn, GroupId g) throws DbException, FormatException { Transaction txn, GroupId g) throws DbException, FormatException {
Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g); Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g);
Map<MessageId, BdfDictionary> parsed = new HashMap<>(raw.size()); Map<MessageId, BdfDictionary> parsed =
new HashMap<MessageId, BdfDictionary>(raw.size());
for (Entry<MessageId, Metadata> e : raw.entrySet()) for (Entry<MessageId, Metadata> e : raw.entrySet())
parsed.put(e.getKey(), metadataParser.parse(e.getValue())); parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
return parsed; return parsed;
@@ -228,7 +229,8 @@ class ClientHelperImpl implements ClientHelper {
FormatException { FormatException {
Metadata metadata = metadataEncoder.encode(query); Metadata metadata = metadataEncoder.encode(query);
Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g, metadata); Map<MessageId, Metadata> raw = db.getMessageMetadata(txn, g, metadata);
Map<MessageId, BdfDictionary> parsed = new HashMap<>(raw.size()); Map<MessageId, BdfDictionary> parsed =
new HashMap<MessageId, BdfDictionary>(raw.size());
for (Entry<MessageId, Metadata> e : raw.entrySet()) for (Entry<MessageId, Metadata> e : raw.entrySet())
parsed.put(e.getKey(), metadataParser.parse(e.getValue())); parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
return parsed; return parsed;

View File

@@ -32,25 +32,23 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
} }
@Override @Override
public Group createLocalGroup(ClientId clientId, int clientVersion) { public Group createLocalGroup(ClientId clientId) {
return groupFactory.createGroup(clientId, clientVersion, return groupFactory.createGroup(clientId, LOCAL_GROUP_DESCRIPTOR);
LOCAL_GROUP_DESCRIPTOR);
} }
@Override @Override
public Group createContactGroup(ClientId clientId, int clientVersion, public Group createContactGroup(ClientId clientId, Contact contact) {
Contact contact) {
AuthorId local = contact.getLocalAuthorId(); AuthorId local = contact.getLocalAuthorId();
AuthorId remote = contact.getAuthor().getId(); AuthorId remote = contact.getAuthor().getId();
byte[] descriptor = createGroupDescriptor(local, remote); byte[] descriptor = createGroupDescriptor(local, remote);
return groupFactory.createGroup(clientId, clientVersion, descriptor); return groupFactory.createGroup(clientId, descriptor);
} }
@Override @Override
public Group createContactGroup(ClientId clientId, int clientVersion, public Group createContactGroup(ClientId clientId, AuthorId authorId1,
AuthorId authorId1, AuthorId authorId2) { AuthorId authorId2) {
byte[] descriptor = createGroupDescriptor(authorId1, authorId2); byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
return groupFactory.createGroup(clientId, clientVersion, descriptor); return groupFactory.createGroup(clientId, descriptor);
} }
private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) { private byte[] createGroupDescriptor(AuthorId local, AuthorId remote) {

View File

@@ -141,10 +141,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} }
// Derive the header keys for the transport streams // Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
masterSecret, new byte[] {PROTOCOL_VERSION}); SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
// Create the readers // Create the readers
InputStream streamReader = InputStream streamReader =
@@ -158,10 +156,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
BdfWriter w = bdfWriterFactory.createWriter(streamWriter); BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
// Derive the nonces to be signed // Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret, byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
new byte[] {PROTOCOL_VERSION}); byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
// Exchange pseudonyms, signed nonces, and timestamps // Exchange pseudonyms, signed nonces, and timestamps
long localTimestamp = clock.currentTimeMillis(); long localTimestamp = clock.currentTimeMillis();
@@ -188,7 +184,12 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
// Close the outgoing stream and expect EOF on the incoming stream // Close the outgoing stream and expect EOF on the incoming stream
w.close(); w.close();
if (!r.eof()) LOG.warning("Unexpected data at end of connection"); if (!r.eof()) LOG.warning("Unexpected data at end of connection");
} catch (GeneralSecurityException | IOException e) { } catch (GeneralSecurityException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed();
tryToClose(conn, true);
return;
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
listener.contactExchangeFailed(); listener.contactExchangeFailed();
tryToClose(conn, true); tryToClose(conn, true);
@@ -200,8 +201,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
try { try {
// Add the contact // Add the contact
ContactId contactId = addContact(remoteAuthor, timestamp, ContactId contactId = addContact(remoteAuthor, masterSecret,
remoteProperties); timestamp, alice, remoteProperties);
// Reuse the connection as a transport connection // Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contactId, transportId, connectionManager.manageOutgoingConnection(contactId, transportId,
conn); conn);
@@ -275,7 +276,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private Map<TransportId, TransportProperties> receiveTransportProperties( private Map<TransportId, TransportProperties> receiveTransportProperties(
BdfReader r) throws IOException { BdfReader r) throws IOException {
Map<TransportId, TransportProperties> remote = new HashMap<>(); Map<TransportId, TransportProperties> remote =
new HashMap<TransportId, TransportProperties>();
r.readListStart(); r.readListStart();
while (!r.hasListEnd()) { while (!r.hasListEnd()) {
r.readListStart(); r.readListStart();
@@ -298,15 +300,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
return remote; return remote;
} }
private ContactId addContact(Author remoteAuthor, long timestamp, private ContactId addContact(Author remoteAuthor, SecretKey master,
long timestamp, boolean alice,
Map<TransportId, TransportProperties> remoteProperties) Map<TransportId, TransportProperties> remoteProperties)
throws DbException { throws DbException {
ContactId contactId; ContactId contactId;
Transaction txn = db.startTransaction(false); Transaction txn = db.startTransaction(false);
try { try {
contactId = contactManager.addContact(txn, remoteAuthor, contactId = contactManager.addContact(txn, remoteAuthor,
localAuthor.getId(), masterSecret, timestamp, alice, localAuthor.getId(), master, timestamp, alice, true, true);
true, true);
transportPropertyManager.addRemoteProperties(txn, contactId, transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties); remoteProperties);
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -316,7 +318,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
return contactId; return contactId;
} }
private void tryToClose(DuplexTransportConnection conn, boolean exception) { private void tryToClose(DuplexTransportConnection conn,
boolean exception) {
try { try {
LOG.info("Closing connection"); LOG.info("Closing connection");
conn.getReader().dispose(exception, true); conn.getReader().dispose(exception, true);

View File

@@ -34,8 +34,8 @@ class ContactManagerImpl implements ContactManager {
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) { ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) {
this.db = db; this.db = db;
this.keyManager = keyManager; this.keyManager = keyManager;
addHooks = new CopyOnWriteArrayList<>(); addHooks = new CopyOnWriteArrayList<AddContactHook>();
removeHooks = new CopyOnWriteArrayList<>(); removeHooks = new CopyOnWriteArrayList<RemoveContactHook>();
} }
@Override @Override
@@ -125,7 +125,7 @@ class ContactManagerImpl implements ContactManager {
} finally { } finally {
db.endTransaction(txn); db.endTransaction(txn);
} }
List<Contact> active = new ArrayList<>(contacts.size()); List<Contact> active = new ArrayList<Contact>(contacts.size());
for (Contact c : contacts) if (c.isActive()) active.add(c); for (Contact c : contacts) if (c.isActive()) active.add(c);
return active; return active;
} }

View File

@@ -1,16 +1,16 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser; import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.util.ByteUtils; import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.spongycastle.crypto.AsymmetricCipherKeyPair; import org.spongycastle.crypto.AsymmetricCipherKeyPair;
@@ -26,6 +26,7 @@ import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters; import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.crypto.params.KeyParameter; import org.spongycastle.crypto.params.KeyParameter;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.Provider; import java.security.Provider;
@@ -39,8 +40,14 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS; import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
class CryptoComponentImpl implements CryptoComponent { class CryptoComponentImpl implements CryptoComponent {
@@ -49,19 +56,49 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int AGREEMENT_KEY_PAIR_BITS = 256; private static final int AGREEMENT_KEY_PAIR_BITS = 256;
private static final int SIGNATURE_KEY_PAIR_BITS = 256; private static final int SIGNATURE_KEY_PAIR_BITS = 256;
private static final int ED_KEY_PAIR_BITS = 256;
private static final int STORAGE_IV_BYTES = 24; // 196 bits private static final int STORAGE_IV_BYTES = 24; // 196 bits
private static final int PBKDF_SALT_BYTES = 32; // 256 bits private static final int PBKDF_SALT_BYTES = 32; // 256 bits
private static final int PBKDF_TARGET_MILLIS = 500; private static final int PBKDF_TARGET_MILLIS = 500;
private static final int PBKDF_SAMPLES = 30; private static final int PBKDF_SAMPLES = 30;
private static final int HASH_SIZE = 256 / 8;
private static byte[] ascii(String s) {
return s.getBytes(Charset.forName("US-ASCII"));
}
// KDF labels for contact exchange stream header key derivation
private static final byte[] A_INVITE = ascii("ALICE_INVITATION_KEY");
private static final byte[] B_INVITE = ascii("BOB_INVITATION_KEY");
// KDF labels for contact exchange signature nonce derivation
private static final byte[] A_SIG_NONCE = ascii("ALICE_SIGNATURE_NONCE");
private static final byte[] B_SIG_NONCE = ascii("BOB_SIGNATURE_NONCE");
// Hash label for BQP public key commitment derivation
private static final String COMMIT =
"org.briarproject.bramble.COMMIT";
// Hash label for shared secret derivation
private static final String SHARED_SECRET =
"org.briarproject.bramble.SHARED_SECRET";
// KDF label for BQP confirmation key derivation
private static final byte[] CONFIRMATION_KEY = ascii("CONFIRMATION_KEY");
// KDF label for master key derivation
private static final byte[] MASTER_KEY = ascii("MASTER_KEY");
// KDF labels for tag key derivation
private static final byte[] A_TAG = ascii("ALICE_TAG_KEY");
private static final byte[] B_TAG = ascii("BOB_TAG_KEY");
// KDF labels for header key derivation
private static final byte[] A_HEADER = ascii("ALICE_HEADER_KEY");
private static final byte[] B_HEADER = ascii("BOB_HEADER_KEY");
// KDF labels for MAC key derivation
private static final byte[] A_MAC = ascii("ALICE_MAC_KEY");
private static final byte[] B_MAC = ascii("BOB_MAC_KEY");
// KDF label for key rotation
private static final byte[] ROTATE = ascii("ROTATE");
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final ECKeyPairGenerator agreementKeyPairGenerator; private final ECKeyPairGenerator agreementKeyPairGenerator;
private final ECKeyPairGenerator signatureKeyPairGenerator; private final ECKeyPairGenerator signatureKeyPairGenerator;
private final KeyParser agreementKeyParser, signatureKeyParser; private final KeyParser agreementKeyParser, signatureKeyParser;
private final MessageEncrypter messageEncrypter; private final MessageEncrypter messageEncrypter;
private final KeyPairGenerator edKeyPairGenerator;
private final KeyParser edKeyParser;
@Inject @Inject
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) { CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
@@ -95,9 +132,6 @@ class CryptoComponentImpl implements CryptoComponent {
signatureKeyParser = new Sec1KeyParser(PARAMETERS, signatureKeyParser = new Sec1KeyParser(PARAMETERS,
SIGNATURE_KEY_PAIR_BITS); SIGNATURE_KEY_PAIR_BITS);
messageEncrypter = new MessageEncrypter(secureRandom); messageEncrypter = new MessageEncrypter(secureRandom);
edKeyPairGenerator = new KeyPairGenerator();
edKeyPairGenerator.initialize(ED_KEY_PAIR_BITS, secureRandom);
edKeyParser = new EdKeyParser();
} }
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html // Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
@@ -156,21 +190,6 @@ class CryptoComponentImpl implements CryptoComponent {
return secret; return secret;
} }
@Override
public KeyPair generateEdKeyPair() {
java.security.KeyPair keyPair = edKeyPairGenerator.generateKeyPair();
EdDSAPublicKey edPublicKey = (EdDSAPublicKey) keyPair.getPublic();
PublicKey publicKey = new EdPublicKey(edPublicKey.getAbyte());
EdDSAPrivateKey edPrivateKey = (EdDSAPrivateKey) keyPair.getPrivate();
PrivateKey privateKey = new EdPrivateKey(edPrivateKey.getSeed());
return new KeyPair(publicKey, privateKey);
}
@Override
public KeyParser getEdKeyParser() {
return edKeyParser;
}
@Override @Override
public KeyPair generateAgreementKeyPair() { public KeyPair generateAgreementKeyPair() {
AsymmetricCipherKeyPair keyPair = AsymmetricCipherKeyPair keyPair =
@@ -219,63 +238,197 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) { public SecretKey deriveHeaderKey(SecretKey master,
byte[] mac = mac(label, k, inputs); boolean alice) {
if (mac.length != SecretKey.LENGTH) throw new IllegalStateException(); return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
return new SecretKey(mac);
} }
@Override @Override
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey, public SecretKey deriveMacKey(SecretKey master, boolean alice) {
KeyPair ourKeyPair, byte[]... inputs) return new SecretKey(macKdf(master, alice ? A_MAC : B_MAC));
throws GeneralSecurityException { }
@Override
public byte[] deriveSignatureNonce(SecretKey master,
boolean alice) {
return macKdf(master, alice ? A_SIG_NONCE : B_SIG_NONCE);
}
@Override
public byte[] deriveKeyCommitment(byte[] publicKey) {
byte[] hash = hash(COMMIT, publicKey);
// The output is the first COMMIT_LENGTH bytes of the hash
byte[] commitment = new byte[COMMIT_LENGTH];
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
return commitment;
}
@Override
public SecretKey deriveSharedSecret(byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
PrivateKey ourPriv = ourKeyPair.getPrivate(); PrivateKey ourPriv = ourKeyPair.getPrivate();
byte[][] hashInputs = new byte[inputs.length + 1][]; PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
hashInputs[0] = performRawKeyAgreement(ourPriv, theirPublicKey); byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
System.arraycopy(inputs, 0, hashInputs, 1, inputs.length); byte[] alicePub, bobPub;
byte[] hash = hash(label, hashInputs); if (alice) {
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException(); alicePub = ourKeyPair.getPublic().getEncoded();
return new SecretKey(hash); bobPub = theirPublicKey;
} else {
alicePub = theirPublicKey;
bobPub = ourKeyPair.getPublic().getEncoded();
}
return new SecretKey(hash(SHARED_SECRET, raw, alicePub, bobPub));
}
@Override
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload, byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
SecretKey ck = new SecretKey(macKdf(sharedSecret, CONFIRMATION_KEY));
byte[] alicePayload, alicePub, bobPayload, bobPub;
if (alice) {
alicePayload = ourPayload;
alicePub = ourKeyPair.getPublic().getEncoded();
bobPayload = theirPayload;
bobPub = theirPublicKey;
} else {
alicePayload = theirPayload;
alicePub = theirPublicKey;
bobPayload = ourPayload;
bobPub = ourKeyPair.getPublic().getEncoded();
}
if (aliceRecord)
return macKdf(ck, alicePayload, alicePub, bobPayload, bobPub);
else
return macKdf(ck, bobPayload, bobPub, alicePayload, alicePub);
}
@Override
public SecretKey deriveMasterSecret(SecretKey sharedSecret) {
return new SecretKey(macKdf(sharedSecret, MASTER_KEY));
}
@Override
public SecretKey deriveMasterSecret(byte[] theirPublicKey,
KeyPair ourKeyPair, boolean alice) throws GeneralSecurityException {
return deriveMasterSecret(deriveSharedSecret(
theirPublicKey, ourKeyPair, alice));
}
@Override
public TransportKeys deriveTransportKeys(TransportId t,
SecretKey master, long rotationPeriod, boolean alice) {
// Keys for the previous period are derived from the master secret
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
SecretKey outTagPrev = deriveTagKey(master, t, alice);
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
// Derive the keys for the current and next periods
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
// Initialise the reordering windows and stream counters
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
rotationPeriod - 1);
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
rotationPeriod);
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
rotationPeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
rotationPeriod);
// Collect and return the keys
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
}
@Override
public TransportKeys rotateTransportKeys(TransportKeys k,
long rotationPeriod) {
if (k.getRotationPeriod() >= rotationPeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getRotationPeriod();
// Rotate the keys
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
inPrev = inCurr;
inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p);
}
// Collect and return the keys
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
outCurr);
}
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
byte[] period = new byte[INT_64_BYTES];
ByteUtils.writeUint64(rotationPeriod, period, 0);
return new SecretKey(macKdf(k, ROTATE, period));
}
private SecretKey deriveTagKey(SecretKey master, TransportId t,
boolean alice) {
byte[] id = StringUtils.toUtf8(t.getString());
return new SecretKey(macKdf(master, alice ? A_TAG : B_TAG, id));
}
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
boolean alice) {
byte[] id = StringUtils.toUtf8(t.getString());
return new SecretKey(macKdf(master, alice ? A_HEADER : B_HEADER, id));
}
@Override
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber) {
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
throw new IllegalArgumentException();
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
// Initialise the PRF
Digest prf = new Blake2sDigest(tagKey.getBytes());
// The output of the PRF must be long enough to use as a tag
int macLength = prf.getDigestSize();
if (macLength < TAG_LENGTH) throw new IllegalStateException();
// The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
byte[] mac = new byte[macLength];
prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
} }
@Override @Override
public byte[] sign(String label, byte[] toSign, byte[] privateKey) public byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException { throws GeneralSecurityException {
return sign(new SignatureImpl(secureRandom), signatureKeyParser, label, Signature signature = new SignatureImpl(secureRandom);
toSign, privateKey); KeyParser keyParser = getSignatureKeyParser();
}
@Override
public byte[] signEd(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException {
return sign(new EdSignature(), edKeyParser, label, toSign, privateKey);
}
private byte[] sign(Signature sig, KeyParser keyParser, String label,
byte[] toSign, byte[] privateKey) throws GeneralSecurityException {
PrivateKey key = keyParser.parsePrivateKey(privateKey); PrivateKey key = keyParser.parsePrivateKey(privateKey);
sig.initSign(key); signature.initSign(key);
updateSignature(sig, label, toSign); updateSignature(signature, label, toSign);
return sig.sign(); return signature.sign();
} }
@Override @Override
public boolean verify(String label, byte[] signedData, byte[] publicKey, public boolean verify(String label, byte[] signedData, byte[] publicKey,
byte[] signature) throws GeneralSecurityException { byte[] signature) throws GeneralSecurityException {
return verify(new SignatureImpl(secureRandom), signatureKeyParser, Signature sig = new SignatureImpl(secureRandom);
label, signedData, publicKey, signature); KeyParser keyParser = getSignatureKeyParser();
}
@Override
public boolean verifyEd(String label, byte[] signedData, byte[] publicKey,
byte[] signature) throws GeneralSecurityException {
return verify(new EdSignature(), edKeyParser, label, signedData,
publicKey, signature);
}
private boolean verify(Signature sig, KeyParser keyParser, String label,
byte[] signedData, byte[] publicKey, byte[] signature)
throws GeneralSecurityException {
PublicKey key = keyParser.parsePublicKey(publicKey); PublicKey key = keyParser.parsePublicKey(publicKey);
sig.initVerify(key); sig.initVerify(key);
updateSignature(sig, label, signedData); updateSignature(sig, label, signedData);
@@ -283,7 +436,7 @@ class CryptoComponentImpl implements CryptoComponent {
} }
private void updateSignature(Signature signature, String label, private void updateSignature(Signature signature, String label,
byte[] toSign) throws GeneralSecurityException { byte[] toSign) {
byte[] labelBytes = StringUtils.toUtf8(label); byte[] labelBytes = StringUtils.toUtf8(label);
byte[] length = new byte[INT_32_BYTES]; byte[] length = new byte[INT_32_BYTES];
ByteUtils.writeUint32(labelBytes.length, length, 0); ByteUtils.writeUint32(labelBytes.length, length, 0);
@@ -313,13 +466,14 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) { public int getHashLength() {
byte[] labelBytes = StringUtils.toUtf8(label); return HASH_SIZE;
}
@Override
public byte[] mac(SecretKey macKey, byte[]... inputs) {
Digest mac = new Blake2sDigest(macKey.getBytes()); Digest mac = new Blake2sDigest(macKey.getBytes());
byte[] length = new byte[INT_32_BYTES]; byte[] length = new byte[INT_32_BYTES];
ByteUtils.writeUint32(labelBytes.length, length, 0);
mac.update(length, 0, length.length);
mac.update(labelBytes, 0, labelBytes.length);
for (byte[] input : inputs) { for (byte[] input : inputs) {
ByteUtils.writeUint32(input.length, length, 0); ByteUtils.writeUint32(input.length, length, 0);
mac.update(length, 0, length.length); mac.update(length, 0, length.length);
@@ -411,6 +565,30 @@ class CryptoComponentImpl implements CryptoComponent {
return AsciiArmour.wrap(b, lineLength); return AsciiArmour.wrap(b, lineLength);
} }
// Key derivation function based on a pseudo-random function - see
// NIST SP 800-108, section 5.1
private byte[] macKdf(SecretKey key, byte[]... inputs) {
// Initialise the PRF
Digest prf = new Blake2sDigest(key.getBytes());
// The output of the PRF must be long enough to use as a key
int macLength = prf.getDigestSize();
if (macLength < SecretKey.LENGTH) throw new IllegalStateException();
// Calculate the PRF over the concatenated length-prefixed inputs
byte[] length = new byte[INT_32_BYTES];
for (byte[] input : inputs) {
ByteUtils.writeUint32(input.length, length, 0);
prf.update(length, 0, length.length);
prf.update(input, 0, input.length);
}
byte[] mac = new byte[macLength];
prf.doFinal(mac, 0);
// The output is the first SecretKey.LENGTH bytes of the MAC
if (mac.length == SecretKey.LENGTH) return mac;
byte[] truncated = new byte[SecretKey.LENGTH];
System.arraycopy(mac, 0, truncated, 0, truncated.length);
return truncated;
}
// Password-based key derivation function - see PKCS#5 v2.1, section 5.2 // Password-based key derivation function - see PKCS#5 v2.1, section 5.2
private byte[] pbkdf2(String password, byte[] salt, int iterations) { private byte[] pbkdf2(String password, byte[] salt, int iterations) {
byte[] utf8 = StringUtils.toUtf8(password); byte[] utf8 = StringUtils.toUtf8(password);
@@ -424,8 +602,8 @@ class CryptoComponentImpl implements CryptoComponent {
// Package access for testing // Package access for testing
int chooseIterationCount(int targetMillis) { int chooseIterationCount(int targetMillis) {
List<Long> quickSamples = new ArrayList<>(PBKDF_SAMPLES); List<Long> quickSamples = new ArrayList<Long>(PBKDF_SAMPLES);
List<Long> slowSamples = new ArrayList<>(PBKDF_SAMPLES); List<Long> slowSamples = new ArrayList<Long>(PBKDF_SAMPLES);
long iterationNanos = 0, initNanos = 0; long iterationNanos = 0, initNanos = 0;
while (iterationNanos <= 0 || initNanos <= 0) { while (iterationNanos <= 0 || initNanos <= 0) {
// Sample the running time with one iteration and two iterations // Sample the running time with one iteration and two iterations

View File

@@ -3,11 +3,9 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.TimeLoggingExecutor; import org.briarproject.bramble.TimeLoggingExecutor;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory; import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory; import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
@@ -50,7 +48,7 @@ public class CryptoModule {
public CryptoModule() { public CryptoModule() {
// Use an unbounded queue // Use an unbounded queue
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(); BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
// Discard tasks that are submitted during shutdown // Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy = RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy(); new ThreadPoolExecutor.DiscardPolicy();
@@ -76,12 +74,6 @@ public class CryptoModule {
return new PasswordStrengthEstimatorImpl(); return new PasswordStrengthEstimatorImpl();
} }
@Provides
TransportCrypto provideTransportCrypto(
TransportCryptoImpl transportCrypto) {
return transportCrypto;
}
@Provides @Provides
StreamDecrypterFactory provideStreamDecrypterFactory( StreamDecrypterFactory provideStreamDecrypterFactory(
Provider<AuthenticatedCipher> cipherProvider) { Provider<AuthenticatedCipher> cipherProvider) {
@@ -89,17 +81,9 @@ public class CryptoModule {
} }
@Provides @Provides
StreamEncrypterFactory provideStreamEncrypterFactory( StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto,
CryptoComponent crypto, TransportCrypto transportCrypto,
Provider<AuthenticatedCipher> cipherProvider) { Provider<AuthenticatedCipher> cipherProvider) {
return new StreamEncrypterFactoryImpl(crypto, transportCrypto, return new StreamEncrypterFactoryImpl(crypto, cipherProvider);
cipherProvider);
}
@Provides
KeyAgreementCrypto provideKeyAgreementCrypto(
KeyAgreementCryptoImpl keyAgreementCrypto) {
return keyAgreementCrypto;
} }
@Provides @Provides

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException;
@NotNullByDefault
class EdKeyParser implements KeyParser {
@Override
public PublicKey parsePublicKey(byte[] encodedKey)
throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException();
return new EdPublicKey(encodedKey);
}
@Override
public PrivateKey parsePrivateKey(byte[] encodedKey)
throws GeneralSecurityException {
if (encodedKey.length != 32) throw new GeneralSecurityException();
return new EdPrivateKey(encodedKey);
}
}

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class EdPrivateKey extends Bytes implements PrivateKey {
EdPrivateKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -1,18 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
class EdPublicKey extends Bytes implements PublicKey {
EdPublicKey(byte[] bytes) {
super(bytes);
}
@Override
public byte[] getEncoded() {
return getBytes();
}
}

View File

@@ -1,83 +0,0 @@
package org.briarproject.bramble.crypto;
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
import net.i2p.crypto.eddsa.EdDSAPublicKey;
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveSpec;
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
@NotNullByDefault
class EdSignature implements Signature {
private static final Provider PROVIDER = new EdDSASecurityProvider();
private static final EdDSANamedCurveSpec CURVE_SPEC =
EdDSANamedCurveTable.getByName("Ed25519");
private final java.security.Signature signature;
EdSignature() {
try {
signature = java.security.Signature
.getInstance(SIGNATURE_ALGORITHM, PROVIDER);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
@Override
public void initSign(PrivateKey k) throws GeneralSecurityException {
if (!(k instanceof EdPrivateKey))
throw new IllegalArgumentException();
EdDSAPrivateKey privateKey = new EdDSAPrivateKey(
new EdDSAPrivateKeySpec(k.getEncoded(), CURVE_SPEC));
signature.initSign(privateKey);
}
@Override
public void initVerify(PublicKey k) throws GeneralSecurityException {
if (!(k instanceof EdPublicKey))
throw new IllegalArgumentException();
EdDSAPublicKey publicKey = new EdDSAPublicKey(
new EdDSAPublicKeySpec(k.getEncoded(), CURVE_SPEC));
signature.initVerify(publicKey);
}
@Override
public void update(byte b) throws GeneralSecurityException {
signature.update(b);
}
@Override
public void update(byte[] b) throws GeneralSecurityException {
signature.update(b);
}
@Override
public void update(byte[] b, int off, int len)
throws GeneralSecurityException {
signature.update(b, off, len);
}
@Override
public byte[] sign() throws GeneralSecurityException {
return signature.sign();
}
@Override
public boolean verify(byte[] sig) throws GeneralSecurityException {
return signature.verify(sig);
}
}

View File

@@ -1,56 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import javax.inject.Inject;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
class KeyAgreementCryptoImpl implements KeyAgreementCrypto {
private final CryptoComponent crypto;
@Inject
KeyAgreementCryptoImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
@Override
public byte[] deriveKeyCommitment(PublicKey publicKey) {
byte[] hash = crypto.hash(COMMIT_LABEL, publicKey.getEncoded());
// The output is the first COMMIT_LENGTH bytes of the hash
byte[] commitment = new byte[COMMIT_LENGTH];
System.arraycopy(hash, 0, commitment, 0, COMMIT_LENGTH);
return commitment;
}
@Override
public byte[] deriveConfirmationRecord(SecretKey sharedSecret,
byte[] theirPayload, byte[] ourPayload, PublicKey theirPublicKey,
KeyPair ourKeyPair, boolean alice, boolean aliceRecord) {
SecretKey ck = crypto.deriveKey(CONFIRMATION_KEY_LABEL, sharedSecret);
byte[] alicePayload, alicePub, bobPayload, bobPub;
if (alice) {
alicePayload = ourPayload;
alicePub = ourKeyPair.getPublic().getEncoded();
bobPayload = theirPayload;
bobPub = theirPublicKey.getEncoded();
} else {
alicePayload = theirPayload;
alicePub = theirPublicKey.getEncoded();
bobPayload = ourPayload;
bobPub = ourKeyPair.getPublic().getEncoded();
}
if (aliceRecord) {
return crypto.mac(CONFIRMATION_MAC_LABEL, ck, alicePayload,
alicePub, bobPayload, bobPub);
} else {
return crypto.mac(CONFIRMATION_MAC_LABEL, ck, bobPayload, bobPub,
alicePayload, alicePub);
}
}
}

View File

@@ -16,7 +16,7 @@ class PasswordStrengthEstimatorImpl implements PasswordStrengthEstimator {
@Override @Override
public float estimateStrength(String password) { public float estimateStrength(String password) {
HashSet<Character> unique = new HashSet<>(); HashSet<Character> unique = new HashSet<Character>();
int length = password.length(); int length = password.length();
for (int i = 0; i < length; i++) unique.add(password.charAt(i)); for (int i = 0; i < length; i++) unique.add(password.charAt(i));
return Math.min(1, (float) unique.size() / STRONG_UNIQUE_CHARS); return Math.min(1, (float) unique.size() / STRONG_UNIQUE_CHARS);

View File

@@ -22,25 +22,25 @@ interface Signature {
/** /**
* @see {@link java.security.Signature#update(byte)} * @see {@link java.security.Signature#update(byte)}
*/ */
void update(byte b) throws GeneralSecurityException; void update(byte b);
/** /**
* @see {@link java.security.Signature#update(byte[])} * @see {@link java.security.Signature#update(byte[])}
*/ */
void update(byte[] b) throws GeneralSecurityException; void update(byte[] b);
/** /**
* @see {@link java.security.Signature#update(byte[], int, int)} * @see {@link java.security.Signature#update(byte[], int, int)}
*/ */
void update(byte[] b, int off, int len) throws GeneralSecurityException; void update(byte[] b, int off, int len);
/** /**
* @see {@link java.security.Signature#sign()} * @see {@link java.security.Signature#sign()}
*/ */
byte[] sign() throws GeneralSecurityException; byte[] sign();
/** /**
* @see {@link java.security.Signature#verify(byte[])} * @see {@link java.security.Signature#verify(byte[])}
*/ */
boolean verify(byte[] signature) throws GeneralSecurityException; boolean verify(byte[] signature);
} }

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamEncrypter; import org.briarproject.bramble.api.crypto.StreamEncrypter;
import org.briarproject.bramble.api.crypto.StreamEncrypterFactory; import org.briarproject.bramble.api.crypto.StreamEncrypterFactory;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
@@ -23,15 +22,12 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory { class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final TransportCrypto transportCrypto;
private final Provider<AuthenticatedCipher> cipherProvider; private final Provider<AuthenticatedCipher> cipherProvider;
@Inject @Inject
StreamEncrypterFactoryImpl(CryptoComponent crypto, StreamEncrypterFactoryImpl(CryptoComponent crypto,
TransportCrypto transportCrypto,
Provider<AuthenticatedCipher> cipherProvider) { Provider<AuthenticatedCipher> cipherProvider) {
this.crypto = crypto; this.crypto = crypto;
this.transportCrypto = transportCrypto;
this.cipherProvider = cipherProvider; this.cipherProvider = cipherProvider;
} }
@@ -41,8 +37,7 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
AuthenticatedCipher cipher = cipherProvider.get(); AuthenticatedCipher cipher = cipherProvider.get();
long streamNumber = ctx.getStreamNumber(); long streamNumber = ctx.getStreamNumber();
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
streamNumber);
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH]; byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce); crypto.getSecureRandom().nextBytes(streamHeaderNonce);
SecretKey frameKey = crypto.generateSecretKey(); SecretKey frameKey = crypto.generateSecretKey();

View File

@@ -1,135 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import org.spongycastle.crypto.Digest;
import javax.inject.Inject;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
class TransportCryptoImpl implements TransportCrypto {
private final CryptoComponent crypto;
@Inject
TransportCryptoImpl(CryptoComponent crypto) {
this.crypto = crypto;
}
@Override
public TransportKeys deriveTransportKeys(TransportId t,
SecretKey master, long rotationPeriod, boolean alice) {
// Keys for the previous period are derived from the master secret
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
SecretKey outTagPrev = deriveTagKey(master, t, alice);
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
// Derive the keys for the current and next periods
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
// Initialise the reordering windows and stream counters
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
rotationPeriod - 1);
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
rotationPeriod);
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
rotationPeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
rotationPeriod);
// Collect and return the keys
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
}
@Override
public TransportKeys rotateTransportKeys(TransportKeys k,
long rotationPeriod) {
if (k.getRotationPeriod() >= rotationPeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getRotationPeriod();
// Rotate the keys
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
inPrev = inCurr;
inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
SecretKey inNextHeader = rotateKey(inNext.getHeaderKey(), p + 1);
inNext = new IncomingKeys(inNextTag, inNextHeader, p + 1);
SecretKey outCurrTag = rotateKey(outCurr.getTagKey(), p);
SecretKey outCurrHeader = rotateKey(outCurr.getHeaderKey(), p);
outCurr = new OutgoingKeys(outCurrTag, outCurrHeader, p);
}
// Collect and return the keys
return new TransportKeys(k.getTransportId(), inPrev, inCurr, inNext,
outCurr);
}
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
byte[] period = new byte[INT_64_BYTES];
ByteUtils.writeUint64(rotationPeriod, period, 0);
return crypto.deriveKey(ROTATE_LABEL, k, period);
}
private SecretKey deriveTagKey(SecretKey master, TransportId t,
boolean alice) {
String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
byte[] id = StringUtils.toUtf8(t.getString());
return crypto.deriveKey(label, master, id);
}
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
boolean alice) {
String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL;
byte[] id = StringUtils.toUtf8(t.getString());
return crypto.deriveKey(label, master, id);
}
@Override
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber) {
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
throw new IllegalArgumentException();
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException();
// Initialise the PRF
Digest prf = new Blake2sDigest(tagKey.getBytes());
// The output of the PRF must be long enough to use as a tag
int macLength = prf.getDigestSize();
if (macLength < TAG_LENGTH) throw new IllegalStateException();
// The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
byte[] mac = new byte[macLength];
prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
}
}

View File

@@ -90,6 +90,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
private final ReentrantReadWriteLock lock = private final ReentrantReadWriteLock lock =
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
private volatile int shutdownHandle = -1;
@Inject @Inject
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus, DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
ShutdownManager shutdown) { ShutdownManager shutdown) {
@@ -101,20 +103,26 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public boolean open() throws DbException { public boolean open() throws DbException {
boolean reopened = db.open(); Runnable shutdownHook = new Runnable() {
shutdown.addShutdownHook(() -> { @Override
try { public void run() {
close(); try {
} catch (DbException e) { close();
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); } catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
} }
}); };
boolean reopened = db.open();
shutdownHandle = shutdown.addShutdownHook(shutdownHook);
return reopened; return reopened;
} }
@Override @Override
public void close() throws DbException { public void close() throws DbException {
if (closed.getAndSet(true)) return; if (closed.getAndSet(true)) return;
shutdown.removeShutdownHook(shutdownHandle);
db.close(); db.close();
} }
@@ -133,7 +141,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
try { try {
return new Transaction(db.startTransaction(), readOnly); return new Transaction(db.startTransaction(), readOnly);
} catch (DbException | RuntimeException e) { } catch (DbException e) {
if (readOnly) lock.readLock().unlock();
else lock.writeLock().unlock();
throw e;
} catch (RuntimeException e) {
if (readOnly) lock.readLock().unlock(); if (readOnly) lock.readLock().unlock();
else lock.writeLock().unlock(); else lock.writeLock().unlock();
throw e; throw e;
@@ -319,7 +331,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
Collection<MessageId> ids = db.getMessagesToSend(txn, c, maxLength); Collection<MessageId> ids = db.getMessagesToSend(txn, c, maxLength);
List<byte[]> messages = new ArrayList<>(ids.size()); List<byte[]> messages = new ArrayList<byte[]>(ids.size());
for (MessageId m : ids) { for (MessageId m : ids) {
messages.add(db.getRawMessage(txn, m)); messages.add(db.getRawMessage(txn, m));
db.updateExpiryTime(txn, c, m, maxLatency); db.updateExpiryTime(txn, c, m, maxLatency);
@@ -369,7 +381,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
throw new NoSuchContactException(); throw new NoSuchContactException();
Collection<MessageId> ids = db.getRequestedMessagesToSend(txn, c, Collection<MessageId> ids = db.getRequestedMessagesToSend(txn, c,
maxLength); maxLength);
List<byte[]> messages = new ArrayList<>(ids.size()); List<byte[]> messages = new ArrayList<byte[]>(ids.size());
for (MessageId m : ids) { for (MessageId m : ids) {
messages.add(db.getRawMessage(txn, m)); messages.add(db.getRawMessage(txn, m));
db.updateExpiryTime(txn, c, m, maxLatency); db.updateExpiryTime(txn, c, m, maxLatency);
@@ -649,7 +661,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
throw new NoSuchContactException(); throw new NoSuchContactException();
Collection<MessageId> acked = new ArrayList<>(); Collection<MessageId> acked = new ArrayList<MessageId>();
for (MessageId m : a.getMessageIds()) { for (MessageId m : a.getMessageIds()) {
if (db.containsVisibleMessage(txn, c, m)) { if (db.containsVisibleMessage(txn, c, m)) {
db.raiseSeenFlag(txn, c, m); db.raiseSeenFlag(txn, c, m);
@@ -758,16 +770,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new LocalAuthorRemovedEvent(a)); transaction.attach(new LocalAuthorRemovedEvent(a));
} }
@Override
public void removeMessage(Transaction transaction, MessageId m)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
db.removeMessage(txn, m);
}
@Override @Override
public void removeTransport(Transaction transaction, TransportId t) public void removeTransport(Transaction transaction, TransportId t)
throws DbException { throws DbException {
@@ -884,7 +886,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
Map<ContactId, TransportKeys> keys) throws DbException { Map<ContactId, TransportKeys> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
Map<ContactId, TransportKeys> filtered = new HashMap<>(); Map<ContactId, TransportKeys> filtered =
new HashMap<ContactId, TransportKeys>();
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) { for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
ContactId c = e.getKey(); ContactId c = e.getKey();
TransportKeys k = e.getValue(); TransportKeys k = e.getValue();

View File

@@ -32,7 +32,7 @@ public class DatabaseExecutorModule {
public DatabaseExecutorModule() { public DatabaseExecutorModule() {
// Use an unbounded queue // Use an unbounded queue
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(); BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
// Discard tasks that are submitted during shutdown // Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy = RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy(); new ThreadPoolExecutor.DiscardPolicy();

View File

@@ -26,7 +26,7 @@ public class DatabaseModule {
@Singleton @Singleton
DatabaseComponent provideDatabaseComponent(Database<Connection> db, DatabaseComponent provideDatabaseComponent(Database<Connection> db,
EventBus eventBus, ShutdownManager shutdown) { EventBus eventBus, ShutdownManager shutdown) {
return new DatabaseComponentImpl<>(db, Connection.class, eventBus, return new DatabaseComponentImpl<Connection>(db, Connection.class,
shutdown); eventBus, shutdown);
} }
} }

View File

@@ -22,23 +22,21 @@ import javax.inject.Inject;
class H2Database extends JdbcDatabase { class H2Database extends JdbcDatabase {
private static final String HASH_TYPE = "BINARY(32)"; private static final String HASH_TYPE = "BINARY(32)";
private static final String SECRET_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY"; private static final String BINARY_TYPE = "BINARY";
private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT"; private static final String COUNTER_TYPE = "INT NOT NULL AUTO_INCREMENT";
private static final String STRING_TYPE = "VARCHAR"; private static final String SECRET_TYPE = "BINARY(32)";
private final DatabaseConfig config; private final DatabaseConfig config;
private final String url; private final String url;
@Inject @Inject
H2Database(DatabaseConfig config, Clock clock) { H2Database(DatabaseConfig config, Clock clock) {
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE, super(HASH_TYPE, BINARY_TYPE, COUNTER_TYPE, SECRET_TYPE, clock);
clock);
this.config = config; this.config = config;
File dir = config.getDatabaseDirectory(); File dir = config.getDatabaseDirectory();
String path = new File(dir, "db").getAbsolutePath(); String path = new File(dir, "db").getAbsolutePath();
url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1" url = "jdbc:h2:split:" + path + ";CIPHER=AES;MULTI_THREADED=1"
+ ";WRITE_DELAY=0"; + ";WRITE_DELAY=0;DB_CLOSE_ON_EXIT=false";
} }
@Override @Override

View File

@@ -1,99 +0,0 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.StringUtils;
import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import javax.inject.Inject;
/**
* Contains all the HSQLDB-specific code for the database.
*/
@NotNullByDefault
class HyperSqlDatabase extends JdbcDatabase {
private static final String HASH_TYPE = "BINARY(32)";
private static final String SECRET_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY";
private static final String COUNTER_TYPE =
"INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY(START WITH 1)";
private static final String STRING_TYPE = "VARCHAR";
private final DatabaseConfig config;
private final String url;
@Inject
HyperSqlDatabase(DatabaseConfig config, Clock clock) {
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
clock);
this.config = config;
File dir = config.getDatabaseDirectory();
String path = new File(dir, "db").getAbsolutePath();
url = "jdbc:hsqldb:file:" + path
+ ";sql.enforce_size=false;allow_empty_batch=true"
+ ";encrypt_lobs=true;crypt_type=AES";
}
@Override
public boolean open() throws DbException {
boolean reopen = config.databaseExists();
if (!reopen) config.getDatabaseDirectory().mkdirs();
super.open("org.hsqldb.jdbc.JDBCDriver", reopen);
return reopen;
}
@Override
public void close() throws DbException {
try {
super.closeAllConnections();
Connection c = createConnection();
Statement s = c.createStatement();
s.executeQuery("SHUTDOWN");
s.close();
c.close();
} catch (SQLException e) {
throw new DbException(e);
}
}
@Override
public long getFreeSpace() throws DbException {
File dir = config.getDatabaseDirectory();
long maxSize = config.getMaxSize();
long free = dir.getFreeSpace();
long used = getDiskSpace(dir);
long quota = maxSize - used;
return Math.min(free, quota);
}
private long getDiskSpace(File f) {
if (f.isDirectory()) {
long total = 0;
File[] children = f.listFiles();
if (children != null)
for (File child : children) total += getDiskSpace(child);
return total;
} else if (f.isFile()) {
return f.length();
} else {
return 0;
}
}
@Override
protected Connection createConnection() throws SQLException {
SecretKey key = config.getEncryptionKey();
if (key == null) throw new IllegalStateException();
String hex = StringUtils.toHexString(key.getBytes());
return DriverManager.getConnection(url + ";crypt_key=" + hex);
}
}

View File

@@ -68,32 +68,32 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
@NotNullByDefault @NotNullByDefault
abstract class JdbcDatabase implements Database<Connection> { abstract class JdbcDatabase implements Database<Connection> {
private static final int SCHEMA_VERSION = 31; private static final int SCHEMA_VERSION = 30;
private static final int MIN_SCHEMA_VERSION = 31; private static final int MIN_SCHEMA_VERSION = 30;
private static final String CREATE_SETTINGS = private static final String CREATE_SETTINGS =
"CREATE TABLE settings" "CREATE TABLE settings"
+ " (namespace _STRING NOT NULL," + " (namespace VARCHAR NOT NULL,"
+ " settingKey _STRING NOT NULL," + " key VARCHAR NOT NULL,"
+ " value _STRING NOT NULL," + " value VARCHAR NOT NULL,"
+ " PRIMARY KEY (namespace, settingKey))"; + " PRIMARY KEY (namespace, key))";
private static final String CREATE_LOCAL_AUTHORS = private static final String CREATE_LOCAL_AUTHORS =
"CREATE TABLE localAuthors" "CREATE TABLE localAuthors"
+ " (authorId _HASH NOT NULL," + " (authorId HASH NOT NULL,"
+ " name _STRING NOT NULL," + " name VARCHAR NOT NULL,"
+ " publicKey _BINARY NOT NULL," + " publicKey BINARY NOT NULL,"
+ " privateKey _BINARY NOT NULL," + " privateKey BINARY NOT NULL,"
+ " created BIGINT NOT NULL," + " created BIGINT NOT NULL,"
+ " PRIMARY KEY (authorId))"; + " PRIMARY KEY (authorId))";
private static final String CREATE_CONTACTS = private static final String CREATE_CONTACTS =
"CREATE TABLE contacts" "CREATE TABLE contacts"
+ " (contactId _COUNTER," + " (contactId COUNTER,"
+ " authorId _HASH NOT NULL," + " authorId HASH NOT NULL,"
+ " name _STRING NOT NULL," + " name VARCHAR NOT NULL,"
+ " publicKey _BINARY NOT NULL," + " publicKey BINARY NOT NULL,"
+ " localAuthorId _HASH NOT NULL," + " localAuthorId HASH NOT NULL,"
+ " verified BOOLEAN NOT NULL," + " verified BOOLEAN NOT NULL,"
+ " active BOOLEAN NOT NULL," + " active BOOLEAN NOT NULL,"
+ " PRIMARY KEY (contactId)," + " PRIMARY KEY (contactId),"
@@ -103,17 +103,17 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_GROUPS = private static final String CREATE_GROUPS =
"CREATE TABLE groups" "CREATE TABLE groups"
+ " (groupId _HASH NOT NULL," + " (groupId HASH NOT NULL,"
+ " clientId _STRING NOT NULL," + " clientId VARCHAR NOT NULL,"
+ " descriptor _BINARY NOT NULL," + " descriptor BINARY NOT NULL,"
+ " PRIMARY KEY (groupId))"; + " PRIMARY KEY (groupId))";
private static final String CREATE_GROUP_METADATA = private static final String CREATE_GROUP_METADATA =
"CREATE TABLE groupMetadata" "CREATE TABLE groupMetadata"
+ " (groupId _HASH NOT NULL," + " (groupId HASH NOT NULL,"
+ " metaKey _STRING NOT NULL," + " key VARCHAR NOT NULL,"
+ " value _BINARY NOT NULL," + " value BINARY NOT NULL,"
+ " PRIMARY KEY (groupId, metaKey)," + " PRIMARY KEY (groupId, key),"
+ " FOREIGN KEY (groupId)" + " FOREIGN KEY (groupId)"
+ " REFERENCES groups (groupId)" + " REFERENCES groups (groupId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
@@ -121,7 +121,7 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_GROUP_VISIBILITIES = private static final String CREATE_GROUP_VISIBILITIES =
"CREATE TABLE groupVisibilities" "CREATE TABLE groupVisibilities"
+ " (contactId INT NOT NULL," + " (contactId INT NOT NULL,"
+ " groupId _HASH NOT NULL," + " groupId HASH NOT NULL,"
+ " shared BOOLEAN NOT NULL," + " shared BOOLEAN NOT NULL,"
+ " PRIMARY KEY (contactId, groupId)," + " PRIMARY KEY (contactId, groupId),"
+ " FOREIGN KEY (contactId)" + " FOREIGN KEY (contactId)"
@@ -133,8 +133,8 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_MESSAGES = private static final String CREATE_MESSAGES =
"CREATE TABLE messages" "CREATE TABLE messages"
+ " (messageId _HASH NOT NULL," + " (messageId HASH NOT NULL,"
+ " groupId _HASH NOT NULL," + " groupId HASH NOT NULL,"
+ " timestamp BIGINT NOT NULL," + " timestamp BIGINT NOT NULL,"
+ " state INT NOT NULL," + " state INT NOT NULL,"
+ " shared BOOLEAN NOT NULL," + " shared BOOLEAN NOT NULL,"
@@ -147,19 +147,19 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_MESSAGE_METADATA = private static final String CREATE_MESSAGE_METADATA =
"CREATE TABLE messageMetadata" "CREATE TABLE messageMetadata"
+ " (messageId _HASH NOT NULL," + " (messageId HASH NOT NULL,"
+ " metaKey _STRING NOT NULL," + " key VARCHAR NOT NULL,"
+ " value _BINARY NOT NULL," + " value BINARY NOT NULL,"
+ " PRIMARY KEY (messageId, metaKey)," + " PRIMARY KEY (messageId, key),"
+ " FOREIGN KEY (messageId)" + " FOREIGN KEY (messageId)"
+ " REFERENCES messages (messageId)" + " REFERENCES messages (messageId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
private static final String CREATE_MESSAGE_DEPENDENCIES = private static final String CREATE_MESSAGE_DEPENDENCIES =
"CREATE TABLE messageDependencies" "CREATE TABLE messageDependencies"
+ " (groupId _HASH NOT NULL," + " (groupId HASH NOT NULL,"
+ " messageId _HASH NOT NULL," + " messageId HASH NOT NULL,"
+ " dependencyId _HASH NOT NULL," // Not a foreign key + " dependencyId HASH NOT NULL," // Not a foreign key
+ " FOREIGN KEY (groupId)" + " FOREIGN KEY (groupId)"
+ " REFERENCES groups (groupId)" + " REFERENCES groups (groupId)"
+ " ON DELETE CASCADE," + " ON DELETE CASCADE,"
@@ -169,7 +169,7 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_OFFERS = private static final String CREATE_OFFERS =
"CREATE TABLE offers" "CREATE TABLE offers"
+ " (messageId _HASH NOT NULL," // Not a foreign key + " (messageId HASH NOT NULL," // Not a foreign key
+ " contactId INT NOT NULL," + " contactId INT NOT NULL,"
+ " PRIMARY KEY (messageId, contactId)," + " PRIMARY KEY (messageId, contactId),"
+ " FOREIGN KEY (contactId)" + " FOREIGN KEY (contactId)"
@@ -178,7 +178,7 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_STATUSES = private static final String CREATE_STATUSES =
"CREATE TABLE statuses" "CREATE TABLE statuses"
+ " (messageId _HASH NOT NULL," + " (messageId HASH NOT NULL,"
+ " contactId INT NOT NULL," + " contactId INT NOT NULL,"
+ " ack BOOLEAN NOT NULL," + " ack BOOLEAN NOT NULL,"
+ " seen BOOLEAN NOT NULL," + " seen BOOLEAN NOT NULL,"
@@ -195,20 +195,20 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_TRANSPORTS = private static final String CREATE_TRANSPORTS =
"CREATE TABLE transports" "CREATE TABLE transports"
+ " (transportId _STRING NOT NULL," + " (transportId VARCHAR NOT NULL,"
+ " maxLatency INT NOT NULL," + " maxLatency INT NOT NULL,"
+ " PRIMARY KEY (transportId))"; + " PRIMARY KEY (transportId))";
private static final String CREATE_INCOMING_KEYS = private static final String CREATE_INCOMING_KEYS =
"CREATE TABLE incomingKeys" "CREATE TABLE incomingKeys"
+ " (contactId INT NOT NULL," + " (contactId INT NOT NULL,"
+ " transportId _STRING NOT NULL," + " transportId VARCHAR NOT NULL,"
+ " rotationPeriod BIGINT NOT NULL," + " period BIGINT NOT NULL,"
+ " tagKey _SECRET NOT NULL," + " tagKey SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL," + " headerKey SECRET NOT NULL,"
+ " base BIGINT NOT NULL," + " base BIGINT NOT NULL,"
+ " bitmap _BINARY NOT NULL," + " bitmap BINARY NOT NULL,"
+ " PRIMARY KEY (contactId, transportId, rotationPeriod)," + " PRIMARY KEY (contactId, transportId, period),"
+ " FOREIGN KEY (contactId)" + " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)" + " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE," + " ON DELETE CASCADE,"
@@ -219,10 +219,10 @@ abstract class JdbcDatabase implements Database<Connection> {
private static final String CREATE_OUTGOING_KEYS = private static final String CREATE_OUTGOING_KEYS =
"CREATE TABLE outgoingKeys" "CREATE TABLE outgoingKeys"
+ " (contactId INT NOT NULL," + " (contactId INT NOT NULL,"
+ " transportId _STRING NOT NULL," + " transportId VARCHAR NOT NULL,"
+ " rotationPeriod BIGINT NOT NULL," + " period BIGINT NOT NULL,"
+ " tagKey _SECRET NOT NULL," + " tagKey SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL," + " headerKey SECRET NOT NULL,"
+ " stream BIGINT NOT NULL," + " stream BIGINT NOT NULL,"
+ " PRIMARY KEY (contactId, transportId)," + " PRIMARY KEY (contactId, transportId),"
+ " FOREIGN KEY (contactId)" + " FOREIGN KEY (contactId)"
@@ -232,40 +232,15 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " REFERENCES transports (transportId)" + " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE)"; + " ON DELETE CASCADE)";
private static final String INDEX_CONTACTS_BY_AUTHOR_ID =
"CREATE INDEX IF NOT EXISTS contactsByAuthorId"
+ " ON contacts (authorId)";
private static final String INDEX_MESSAGES_BY_GROUP_ID =
"CREATE INDEX IF NOT EXISTS messagesByGroupId"
+ " ON messages (groupId)";
private static final String INDEX_OFFERS_BY_CONTACT_ID =
"CREATE INDEX IF NOT EXISTS offersByContactId"
+ " ON offers (contactId)";
private static final String INDEX_GROUPS_BY_CLIENT_ID =
"CREATE INDEX IF NOT EXISTS groupsByClientId"
+ " ON groups (clientId)";
private static final String INDEX_MESSAGE_METADATA_BY_MESSAGE_ID =
"CREATE INDEX IF NOT EXISTS messageMetadataByMessageId"
+ " ON messageMetadata (messageId)";
private static final String INDEX_GROUP_METADATA_BY_GROUP_ID =
"CREATE INDEX IF NOT EXISTS groupMetadataByGroupId"
+ " ON groupMetadata (groupId)";
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(JdbcDatabase.class.getName()); Logger.getLogger(JdbcDatabase.class.getName());
// Different database libraries use different names for certain types // Different database libraries use different names for certain types
private final String hashType, secretType, binaryType; private final String hashType, binaryType, counterType, secretType;
private final String counterType, stringType;
private final Clock clock; private final Clock clock;
// Locking: connectionsLock private final LinkedList<Connection> connections =
private final LinkedList<Connection> connections = new LinkedList<>(); new LinkedList<Connection>(); // Locking: connectionsLock
private int openConnections = 0; // Locking: connectionsLock private int openConnections = 0; // Locking: connectionsLock
private boolean closed = false; // Locking: connectionsLock private boolean closed = false; // Locking: connectionsLock
@@ -276,13 +251,12 @@ abstract class JdbcDatabase implements Database<Connection> {
private final Lock connectionsLock = new ReentrantLock(); private final Lock connectionsLock = new ReentrantLock();
private final Condition connectionsChanged = connectionsLock.newCondition(); private final Condition connectionsChanged = connectionsLock.newCondition();
JdbcDatabase(String hashType, String secretType, String binaryType, JdbcDatabase(String hashType, String binaryType, String counterType,
String counterType, String stringType, Clock clock) { String secretType, Clock clock) {
this.hashType = hashType; this.hashType = hashType;
this.secretType = secretType;
this.binaryType = binaryType; this.binaryType = binaryType;
this.counterType = counterType; this.counterType = counterType;
this.stringType = stringType; this.secretType = secretType;
this.clock = clock; this.clock = clock;
} }
@@ -293,7 +267,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
throw new DbException(e); throw new DbException(e);
} }
// Open the database and create the tables and indexes if necessary // Open the database and create the tables if necessary
Connection txn = startTransaction(); Connection txn = startTransaction();
try { try {
if (reopen) { if (reopen) {
@@ -302,7 +276,6 @@ abstract class JdbcDatabase implements Database<Connection> {
createTables(txn); createTables(txn);
storeSchemaVersion(txn); storeSchemaVersion(txn);
} }
createIndexes(txn);
commitTransaction(txn); commitTransaction(txn);
} catch (DbException e) { } catch (DbException e) {
abortTransaction(txn); abortTransaction(txn);
@@ -367,35 +340,17 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
private void createIndexes(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.executeUpdate(INDEX_CONTACTS_BY_AUTHOR_ID);
s.executeUpdate(INDEX_MESSAGES_BY_GROUP_ID);
s.executeUpdate(INDEX_OFFERS_BY_CONTACT_ID);
s.executeUpdate(INDEX_GROUPS_BY_CLIENT_ID);
s.executeUpdate(INDEX_MESSAGE_METADATA_BY_MESSAGE_ID);
s.executeUpdate(INDEX_GROUP_METADATA_BY_GROUP_ID);
s.close();
} catch (SQLException e) {
tryToClose(s);
throw new DbException(e);
}
}
private String insertTypeNames(String s) { private String insertTypeNames(String s) {
s = s.replaceAll("_HASH", hashType); s = s.replaceAll("HASH", hashType);
s = s.replaceAll("_SECRET", secretType); s = s.replaceAll("BINARY", binaryType);
s = s.replaceAll("_BINARY", binaryType); s = s.replaceAll("COUNTER", counterType);
s = s.replaceAll("_COUNTER", counterType); s = s.replaceAll("SECRET", secretType);
s = s.replaceAll("_STRING", stringType);
return s; return s;
} }
@Override @Override
public Connection startTransaction() throws DbException { public Connection startTransaction() throws DbException {
Connection txn; Connection txn = null;
connectionsLock.lock(); connectionsLock.lock();
try { try {
if (closed) throw new DbClosedException(); if (closed) throw new DbClosedException();
@@ -503,8 +458,7 @@ abstract class JdbcDatabase implements Database<Connection> {
try { try {
// Create a contact row // Create a contact row
String sql = "INSERT INTO contacts" String sql = "INSERT INTO contacts"
+ " (authorId, name, publicKey, localAuthorId," + " (authorId, name, publicKey, localAuthorId, verified, active)"
+ " verified, active)"
+ " VALUES (?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, remote.getId().getBytes()); ps.setBytes(1, remote.getId().getBytes());
@@ -723,7 +677,7 @@ abstract class JdbcDatabase implements Database<Connection> {
try { try {
// Store the incoming keys // Store the incoming keys
String sql = "INSERT INTO incomingKeys (contactId, transportId," String sql = "INSERT INTO incomingKeys (contactId, transportId,"
+ " rotationPeriod, tagKey, headerKey, base, bitmap)" + " period, tagKey, headerKey, base, bitmap)"
+ " VALUES (?, ?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
@@ -758,8 +712,8 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows != 1) throw new DbStateException(); if (rows != 1) throw new DbStateException();
ps.close(); ps.close();
// Store the outgoing keys // Store the outgoing keys
sql = "INSERT INTO outgoingKeys (contactId, transportId," sql = "INSERT INTO outgoingKeys (contactId, transportId, period,"
+ " rotationPeriod, tagKey, headerKey, stream)" + " tagKey, headerKey, stream)"
+ " VALUES (?, ?, ?, ?, ?, ?)"; + " VALUES (?, ?, ?, ?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
@@ -1039,7 +993,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " FROM contacts"; + " FROM contacts";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<Contact> contacts = new ArrayList<>(); List<Contact> contacts = new ArrayList<Contact>();
while (rs.next()) { while (rs.next()) {
ContactId contactId = new ContactId(rs.getInt(1)); ContactId contactId = new ContactId(rs.getInt(1));
AuthorId authorId = new AuthorId(rs.getBytes(2)); AuthorId authorId = new AuthorId(rs.getBytes(2));
@@ -1073,7 +1027,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, local.getBytes()); ps.setBytes(1, local.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<ContactId> ids = new ArrayList<>(); List<ContactId> ids = new ArrayList<ContactId>();
while (rs.next()) ids.add(new ContactId(rs.getInt(1))); while (rs.next()) ids.add(new ContactId(rs.getInt(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1098,7 +1052,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, remote.getBytes()); ps.setBytes(1, remote.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<Contact> contacts = new ArrayList<>(); List<Contact> contacts = new ArrayList<Contact>();
while (rs.next()) { while (rs.next()) {
ContactId c = new ContactId(rs.getInt(1)); ContactId c = new ContactId(rs.getInt(1));
String name = rs.getString(2); String name = rs.getString(2);
@@ -1154,7 +1108,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, c.getString()); ps.setString(1, c.getString());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<Group> groups = new ArrayList<>(); List<Group> groups = new ArrayList<Group>();
while (rs.next()) { while (rs.next()) {
GroupId id = new GroupId(rs.getBytes(1)); GroupId id = new GroupId(rs.getBytes(1));
byte[] descriptor = rs.getBytes(2); byte[] descriptor = rs.getBytes(2);
@@ -1207,7 +1161,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<ContactId> visible = new ArrayList<>(); List<ContactId> visible = new ArrayList<ContactId>();
while (rs.next()) visible.add(new ContactId(rs.getInt(1))); while (rs.next()) visible.add(new ContactId(rs.getInt(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1259,7 +1213,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " FROM localAuthors"; + " FROM localAuthors";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<LocalAuthor> authors = new ArrayList<>(); List<LocalAuthor> authors = new ArrayList<LocalAuthor>();
while (rs.next()) { while (rs.next()) {
AuthorId authorId = new AuthorId(rs.getBytes(1)); AuthorId authorId = new AuthorId(rs.getBytes(1));
String name = rs.getString(2); String name = rs.getString(2);
@@ -1289,7 +1243,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1))); while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1312,7 +1266,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, state.getValue()); ps.setInt(1, state.getValue());
ps.setBytes(2, g.getBytes()); ps.setBytes(2, g.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1))); while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1339,7 +1293,7 @@ abstract class JdbcDatabase implements Database<Connection> {
+ " JOIN messageMetadata AS md" + " JOIN messageMetadata AS md"
+ " ON m.messageId = md.messageId" + " ON m.messageId = md.messageId"
+ " WHERE state = ? AND groupId = ?" + " WHERE state = ? AND groupId = ?"
+ " AND metaKey = ? AND value = ?"; + " AND key = ? AND value = ?";
for (Entry<String, byte[]> e : query.entrySet()) { for (Entry<String, byte[]> e : query.entrySet()) {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, DELIVERED.getValue()); ps.setInt(1, DELIVERED.getValue());
@@ -1347,7 +1301,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setString(3, e.getKey()); ps.setString(3, e.getKey());
ps.setBytes(4, e.getValue()); ps.setBytes(4, e.getValue());
rs = ps.executeQuery(); rs = ps.executeQuery();
Set<MessageId> ids = new HashSet<>(); Set<MessageId> ids = new HashSet<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1))); while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1371,7 +1325,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT m.messageId, metaKey, value" String sql = "SELECT m.messageId, key, value"
+ " FROM messages AS m" + " FROM messages AS m"
+ " JOIN messageMetadata AS md" + " JOIN messageMetadata AS md"
+ " ON m.messageId = md.messageId" + " ON m.messageId = md.messageId"
@@ -1381,7 +1335,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, DELIVERED.getValue()); ps.setInt(1, DELIVERED.getValue());
ps.setBytes(2, g.getBytes()); ps.setBytes(2, g.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
Map<MessageId, Metadata> all = new HashMap<>(); Map<MessageId, Metadata> all = new HashMap<MessageId, Metadata>();
Metadata metadata = null; Metadata metadata = null;
MessageId lastMessageId = null; MessageId lastMessageId = null;
while (rs.next()) { while (rs.next()) {
@@ -1410,7 +1364,8 @@ abstract class JdbcDatabase implements Database<Connection> {
Collection<MessageId> matches = getMessageIds(txn, g, query); Collection<MessageId> matches = getMessageIds(txn, g, query);
if (matches.isEmpty()) return Collections.emptyMap(); if (matches.isEmpty()) return Collections.emptyMap();
// Retrieve the metadata for each match // Retrieve the metadata for each match
Map<MessageId, Metadata> all = new HashMap<>(matches.size()); Map<MessageId, Metadata> all = new HashMap<MessageId, Metadata>(
matches.size());
for (MessageId m : matches) all.put(m, getMessageMetadata(txn, m)); for (MessageId m : matches) all.put(m, getMessageMetadata(txn, m));
return all; return all;
} }
@@ -1421,7 +1376,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT metaKey, value FROM groupMetadata" String sql = "SELECT key, value FROM groupMetadata"
+ " WHERE groupId = ?"; + " WHERE groupId = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
@@ -1444,7 +1399,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT metaKey, value FROM messageMetadata AS md" String sql = "SELECT key, value FROM messageMetadata AS md"
+ " JOIN messages AS m" + " JOIN messages AS m"
+ " ON m.messageId = md.messageId" + " ON m.messageId = md.messageId"
+ " WHERE m.state = ? AND md.messageId = ?"; + " WHERE m.state = ? AND md.messageId = ?";
@@ -1470,7 +1425,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT metaKey, value FROM messageMetadata AS md" String sql = "SELECT key, value FROM messageMetadata AS md"
+ " JOIN messages AS m" + " JOIN messages AS m"
+ " ON m.messageId = md.messageId" + " ON m.messageId = md.messageId"
+ " WHERE (m.state = ? OR m.state = ?)" + " WHERE (m.state = ? OR m.state = ?)"
@@ -1508,7 +1463,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setBytes(1, g.getBytes()); ps.setBytes(1, g.getBytes());
ps.setInt(2, c.getInt()); ps.setInt(2, c.getInt());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageStatus> statuses = new ArrayList<>(); List<MessageStatus> statuses = new ArrayList<MessageStatus>();
while (rs.next()) { while (rs.next()) {
MessageId messageId = new MessageId(rs.getBytes(1)); MessageId messageId = new MessageId(rs.getBytes(1));
boolean sent = rs.getBoolean(2); boolean sent = rs.getBoolean(2);
@@ -1567,7 +1522,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes()); ps.setBytes(1, m.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
Map<MessageId, State> dependencies = new HashMap<>(); Map<MessageId, State> dependencies = new HashMap<MessageId, State>();
while (rs.next()) { while (rs.next()) {
MessageId dependency = new MessageId(rs.getBytes(1)); MessageId dependency = new MessageId(rs.getBytes(1));
State state = State.fromValue(rs.getInt(2)); State state = State.fromValue(rs.getInt(2));
@@ -1605,7 +1560,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, m.getBytes()); ps.setBytes(1, m.getBytes());
rs = ps.executeQuery(); rs = ps.executeQuery();
Map<MessageId, State> dependents = new HashMap<>(); Map<MessageId, State> dependents = new HashMap<MessageId, State>();
while (rs.next()) { while (rs.next()) {
MessageId dependent = new MessageId(rs.getBytes(1)); MessageId dependent = new MessageId(rs.getBytes(1));
State state = State.fromValue(rs.getInt(2)); State state = State.fromValue(rs.getInt(2));
@@ -1657,7 +1612,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setInt(2, maxMessages); ps.setInt(2, maxMessages);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1))); while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1693,7 +1648,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setLong(3, now); ps.setLong(3, now);
ps.setInt(4, maxMessages); ps.setInt(4, maxMessages);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1))); while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1718,7 +1673,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setInt(2, maxMessages); ps.setInt(2, maxMessages);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1))); while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1753,7 +1708,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(2, DELIVERED.getValue()); ps.setInt(2, DELIVERED.getValue());
ps.setLong(3, now); ps.setLong(3, now);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
int total = 0; int total = 0;
while (rs.next()) { while (rs.next()) {
int length = rs.getInt(1); int length = rs.getInt(1);
@@ -1795,7 +1750,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(1, state.getValue()); ps.setInt(1, state.getValue());
ps.setString(2, c.getString()); ps.setString(2, c.getString());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1))); while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1825,7 +1780,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, c.getString()); ps.setString(1, c.getString());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
while (rs.next()) ids.add(new MessageId(rs.getBytes(1))); while (rs.next()) ids.add(new MessageId(rs.getBytes(1)));
rs.close(); rs.close();
ps.close(); ps.close();
@@ -1884,7 +1839,7 @@ abstract class JdbcDatabase implements Database<Connection> {
ps.setInt(2, DELIVERED.getValue()); ps.setInt(2, DELIVERED.getValue());
ps.setLong(3, now); ps.setLong(3, now);
rs = ps.executeQuery(); rs = ps.executeQuery();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
int total = 0; int total = 0;
while (rs.next()) { while (rs.next()) {
int length = rs.getInt(1); int length = rs.getInt(1);
@@ -1908,8 +1863,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
ResultSet rs = null; ResultSet rs = null;
try { try {
String sql = "SELECT settingKey, value FROM settings" String sql = "SELECT key, value FROM settings WHERE namespace = ?";
+ " WHERE namespace = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, namespace); ps.setString(1, namespace);
rs = ps.executeQuery(); rs = ps.executeQuery();
@@ -1932,15 +1886,14 @@ abstract class JdbcDatabase implements Database<Connection> {
ResultSet rs = null; ResultSet rs = null;
try { try {
// Retrieve the incoming keys // Retrieve the incoming keys
String sql = "SELECT rotationPeriod, tagKey, headerKey," String sql = "SELECT period, tagKey, headerKey, base, bitmap"
+ " base, bitmap"
+ " FROM incomingKeys" + " FROM incomingKeys"
+ " WHERE transportId = ?" + " WHERE transportId = ?"
+ " ORDER BY contactId, rotationPeriod"; + " ORDER BY contactId, period";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, t.getString()); ps.setString(1, t.getString());
rs = ps.executeQuery(); rs = ps.executeQuery();
List<IncomingKeys> inKeys = new ArrayList<>(); List<IncomingKeys> inKeys = new ArrayList<IncomingKeys>();
while (rs.next()) { while (rs.next()) {
long rotationPeriod = rs.getLong(1); long rotationPeriod = rs.getLong(1);
SecretKey tagKey = new SecretKey(rs.getBytes(2)); SecretKey tagKey = new SecretKey(rs.getBytes(2));
@@ -1953,14 +1906,15 @@ abstract class JdbcDatabase implements Database<Connection> {
rs.close(); rs.close();
ps.close(); ps.close();
// Retrieve the outgoing keys in the same order // Retrieve the outgoing keys in the same order
sql = "SELECT contactId, rotationPeriod, tagKey, headerKey, stream" sql = "SELECT contactId, period, tagKey, headerKey, stream"
+ " FROM outgoingKeys" + " FROM outgoingKeys"
+ " WHERE transportId = ?" + " WHERE transportId = ?"
+ " ORDER BY contactId, rotationPeriod"; + " ORDER BY contactId, period";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setString(1, t.getString()); ps.setString(1, t.getString());
rs = ps.executeQuery(); rs = ps.executeQuery();
Map<ContactId, TransportKeys> keys = new HashMap<>(); Map<ContactId, TransportKeys> keys =
new HashMap<ContactId, TransportKeys>();
for (int i = 0; rs.next(); i++) { for (int i = 0; rs.next(); i++) {
// There should be three times as many incoming keys // There should be three times as many incoming keys
if (inKeys.size() < (i + 1) * 3) throw new DbStateException(); if (inKeys.size() < (i + 1) * 3) throw new DbStateException();
@@ -1993,8 +1947,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "UPDATE outgoingKeys SET stream = stream + 1" String sql = "UPDATE outgoingKeys SET stream = stream + 1"
+ " WHERE contactId = ? AND transportId = ?" + " WHERE contactId = ? AND transportId = ? AND period = ?";
+ " AND rotationPeriod = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setInt(1, c.getInt()); ps.setInt(1, c.getInt());
ps.setString(2, t.getString()); ps.setString(2, t.getString());
@@ -2079,8 +2032,8 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
// Determine which keys are being removed // Determine which keys are being removed
List<String> removed = new ArrayList<>(); List<String> removed = new ArrayList<String>();
Map<String, byte[]> retained = new HashMap<>(); Map<String, byte[]> retained = new HashMap<String, byte[]>();
for (Entry<String, byte[]> e : meta.entrySet()) { for (Entry<String, byte[]> e : meta.entrySet()) {
if (e.getValue() == REMOVE) removed.add(e.getKey()); if (e.getValue() == REMOVE) removed.add(e.getKey());
else retained.put(e.getKey(), e.getValue()); else retained.put(e.getKey(), e.getValue());
@@ -2088,7 +2041,7 @@ abstract class JdbcDatabase implements Database<Connection> {
// Delete any keys that are being removed // Delete any keys that are being removed
if (!removed.isEmpty()) { if (!removed.isEmpty()) {
String sql = "DELETE FROM " + tableName String sql = "DELETE FROM " + tableName
+ " WHERE " + columnName + " = ? AND metaKey = ?"; + " WHERE " + columnName + " = ? AND key = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, id); ps.setBytes(1, id);
for (String key : removed) { for (String key : removed) {
@@ -2107,7 +2060,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (retained.isEmpty()) return; if (retained.isEmpty()) return;
// Update any keys that already exist // Update any keys that already exist
String sql = "UPDATE " + tableName + " SET value = ?" String sql = "UPDATE " + tableName + " SET value = ?"
+ " WHERE " + columnName + " = ? AND metaKey = ?"; + " WHERE " + columnName + " = ? AND key = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(2, id); ps.setBytes(2, id);
for (Entry<String, byte[]> e : retained.entrySet()) { for (Entry<String, byte[]> e : retained.entrySet()) {
@@ -2124,7 +2077,7 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
// Insert any keys that don't already exist // Insert any keys that don't already exist
sql = "INSERT INTO " + tableName sql = "INSERT INTO " + tableName
+ " (" + columnName + ", metaKey, value)" + " (" + columnName + ", key, value)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setBytes(1, id); ps.setBytes(1, id);
@@ -2156,7 +2109,7 @@ abstract class JdbcDatabase implements Database<Connection> {
try { try {
// Update any settings that already exist // Update any settings that already exist
String sql = "UPDATE settings SET value = ?" String sql = "UPDATE settings SET value = ?"
+ " WHERE namespace = ? AND settingKey = ?"; + " WHERE namespace = ? AND key = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
for (Entry<String, String> e : s.entrySet()) { for (Entry<String, String> e : s.entrySet()) {
ps.setString(1, e.getValue()); ps.setString(1, e.getValue());
@@ -2171,7 +2124,7 @@ abstract class JdbcDatabase implements Database<Connection> {
if (rows > 1) throw new DbStateException(); if (rows > 1) throw new DbStateException();
} }
// Insert any settings that don't already exist // Insert any settings that don't already exist
sql = "INSERT INTO settings (namespace, settingKey, value)" sql = "INSERT INTO settings (namespace, key, value)"
+ " VALUES (?, ?, ?)"; + " VALUES (?, ?, ?)";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
int updateIndex = 0, inserted = 0; int updateIndex = 0, inserted = 0;
@@ -2535,8 +2488,7 @@ abstract class JdbcDatabase implements Database<Connection> {
PreparedStatement ps = null; PreparedStatement ps = null;
try { try {
String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?" String sql = "UPDATE incomingKeys SET base = ?, bitmap = ?"
+ " WHERE contactId = ? AND transportId = ?" + " WHERE contactId = ? AND transportId = ? AND period = ?";
+ " AND rotationPeriod = ?";
ps = txn.prepareStatement(sql); ps = txn.prepareStatement(sql);
ps.setLong(1, base); ps.setLong(1, base);
ps.setBytes(2, bitmap); ps.setBytes(2, bitmap);

View File

@@ -15,7 +15,7 @@ import javax.annotation.concurrent.ThreadSafe;
class EventBusImpl implements EventBus { class EventBusImpl implements EventBus {
private final Collection<EventListener> listeners = private final Collection<EventListener> listeners =
new CopyOnWriteArrayList<>(); new CopyOnWriteArrayList<EventListener>();
@Override @Override
public void addListener(EventListener l) { public void addListener(EventListener l) {

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.keyagreement; package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection; import org.briarproject.bramble.api.keyagreement.KeyAgreementConnection;
@@ -46,34 +46,37 @@ class KeyAgreementConnector {
private final Callbacks callbacks; private final Callbacks callbacks;
private final Clock clock; private final Clock clock;
private final KeyAgreementCrypto keyAgreementCrypto; private final CryptoComponent crypto;
private final PluginManager pluginManager; private final PluginManager pluginManager;
private final CompletionService<KeyAgreementConnection> connect; private final CompletionService<KeyAgreementConnection> connect;
private final List<KeyAgreementListener> listeners = new ArrayList<>(); private final List<KeyAgreementListener> listeners =
new ArrayList<KeyAgreementListener>();
private final List<Future<KeyAgreementConnection>> pending = private final List<Future<KeyAgreementConnection>> pending =
new ArrayList<>(); new ArrayList<Future<KeyAgreementConnection>>();
private volatile boolean connecting = false; private volatile boolean connecting = false;
private volatile boolean alice = false; private volatile boolean alice = false;
KeyAgreementConnector(Callbacks callbacks, Clock clock, KeyAgreementConnector(Callbacks callbacks, Clock clock,
KeyAgreementCrypto keyAgreementCrypto, PluginManager pluginManager, CryptoComponent crypto, PluginManager pluginManager,
Executor ioExecutor) { Executor ioExecutor) {
this.callbacks = callbacks; this.callbacks = callbacks;
this.clock = clock; this.clock = clock;
this.keyAgreementCrypto = keyAgreementCrypto; this.crypto = crypto;
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
connect = new ExecutorCompletionService<>(ioExecutor); connect = new ExecutorCompletionService<KeyAgreementConnection>(
ioExecutor);
} }
public Payload listen(KeyPair localKeyPair) { public Payload listen(KeyPair localKeyPair) {
LOG.info("Starting BQP listeners"); LOG.info("Starting BQP listeners");
// Derive commitment // Derive commitment
byte[] commitment = keyAgreementCrypto.deriveKeyCommitment( byte[] commitment = crypto.deriveKeyCommitment(
localKeyPair.getPublic()); localKeyPair.getPublic().getEncoded());
// Start all listeners and collect their descriptors // Start all listeners and collect their descriptors
List<TransportDescriptor> descriptors = new ArrayList<>(); List<TransportDescriptor> descriptors =
new ArrayList<TransportDescriptor>();
for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) { for (DuplexPlugin plugin : pluginManager.getKeyAgreementPlugins()) {
KeyAgreementListener l = KeyAgreementListener l =
plugin.createKeyAgreementListener(commitment); plugin.createKeyAgreementListener(commitment);
@@ -129,7 +132,10 @@ class KeyAgreementConnector {
LOG.info("Interrupted while waiting for connection"); LOG.info("Interrupted while waiting for connection");
Thread.currentThread().interrupt(); Thread.currentThread().interrupt();
return null; return null;
} catch (ExecutionException | IOException e) { } catch (ExecutionException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null;
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return null; return null;
} finally { } finally {

View File

@@ -1,10 +1,19 @@
package org.briarproject.bramble.keyagreement; package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.data.BdfReaderFactory; import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.data.BdfWriterFactory; import org.briarproject.bramble.api.data.BdfWriterFactory;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser; import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.system.Clock;
import java.util.concurrent.Executor;
import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
@@ -13,9 +22,13 @@ import dagger.Provides;
public class KeyAgreementModule { public class KeyAgreementModule {
@Provides @Provides
KeyAgreementTask provideKeyAgreementTask( @Singleton
KeyAgreementTaskImpl keyAgreementTask) { KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock,
return keyAgreementTask; CryptoComponent crypto, EventBus eventBus,
@IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder,
PluginManager pluginManager) {
return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus,
ioExecutor, payloadEncoder, pluginManager);
} }
@Provides @Provides

View File

@@ -1,10 +1,7 @@
package org.briarproject.bramble.keyagreement; package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.KeyParser;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.keyagreement.Payload; import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder; import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
@@ -14,10 +11,6 @@ import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays; import java.util.Arrays;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
/** /**
* Implementation of the BQP protocol. * Implementation of the BQP protocol.
* <p/> * <p/>
@@ -64,7 +57,6 @@ class KeyAgreementProtocol {
private final Callbacks callbacks; private final Callbacks callbacks;
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final KeyAgreementCrypto keyAgreementCrypto;
private final PayloadEncoder payloadEncoder; private final PayloadEncoder payloadEncoder;
private final KeyAgreementTransport transport; private final KeyAgreementTransport transport;
private final Payload theirPayload, ourPayload; private final Payload theirPayload, ourPayload;
@@ -72,13 +64,11 @@ class KeyAgreementProtocol {
private final boolean alice; private final boolean alice;
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto, KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
KeyAgreementCrypto keyAgreementCrypto,
PayloadEncoder payloadEncoder, KeyAgreementTransport transport, PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair, Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
boolean alice) { boolean alice) {
this.callbacks = callbacks; this.callbacks = callbacks;
this.crypto = crypto; this.crypto = crypto;
this.keyAgreementCrypto = keyAgreementCrypto;
this.payloadEncoder = payloadEncoder; this.payloadEncoder = payloadEncoder;
this.transport = transport; this.transport = transport;
this.theirPayload = theirPayload; this.theirPayload = theirPayload;
@@ -96,7 +86,7 @@ class KeyAgreementProtocol {
*/ */
SecretKey perform() throws AbortException, IOException { SecretKey perform() throws AbortException, IOException {
try { try {
PublicKey theirPublicKey; byte[] theirPublicKey;
if (alice) { if (alice) {
sendKey(); sendKey();
// Alice waits here until Bob obtains her payload. // Alice waits here until Bob obtains her payload.
@@ -114,7 +104,7 @@ class KeyAgreementProtocol {
receiveConfirm(s, theirPublicKey); receiveConfirm(s, theirPublicKey);
sendConfirm(s, theirPublicKey); sendConfirm(s, theirPublicKey);
} }
return crypto.deriveKey(MASTER_SECRET_LABEL, s); return crypto.deriveMasterSecret(s);
} catch (AbortException e) { } catch (AbortException e) {
sendAbort(e.getCause() != null); sendAbort(e.getCause() != null);
throw e; throw e;
@@ -125,41 +115,27 @@ class KeyAgreementProtocol {
transport.sendKey(ourKeyPair.getPublic().getEncoded()); transport.sendKey(ourKeyPair.getPublic().getEncoded());
} }
private PublicKey receiveKey() throws AbortException { private byte[] receiveKey() throws AbortException {
byte[] publicKeyBytes = transport.receiveKey(); byte[] publicKey = transport.receiveKey();
callbacks.initialRecordReceived(); callbacks.initialRecordReceived();
KeyParser keyParser = crypto.getAgreementKeyParser(); byte[] expected = crypto.deriveKeyCommitment(publicKey);
try { if (!Arrays.equals(expected, theirPayload.getCommitment()))
PublicKey publicKey = keyParser.parsePublicKey(publicKeyBytes);
byte[] expected = keyAgreementCrypto.deriveKeyCommitment(publicKey);
if (!Arrays.equals(expected, theirPayload.getCommitment()))
throw new AbortException();
return publicKey;
} catch (GeneralSecurityException e) {
throw new AbortException(); throw new AbortException();
} return publicKey;
} }
private SecretKey deriveSharedSecret(PublicKey theirPublicKey) private SecretKey deriveSharedSecret(byte[] theirPublicKey)
throws AbortException { throws AbortException {
try { try {
byte[] ourPublicKeyBytes = ourKeyPair.getPublic().getEncoded(); return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice);
byte[] theirPublicKeyBytes = theirPublicKey.getEncoded();
byte[][] inputs = {
new byte[] {PROTOCOL_VERSION},
alice ? ourPublicKeyBytes : theirPublicKeyBytes,
alice ? theirPublicKeyBytes : ourPublicKeyBytes
};
return crypto.deriveSharedSecret(SHARED_SECRET_LABEL,
theirPublicKey, ourKeyPair, inputs);
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new AbortException(e); throw new AbortException(e);
} }
} }
private void sendConfirm(SecretKey s, PublicKey theirPublicKey) private void sendConfirm(SecretKey s, byte[] theirPublicKey)
throws IOException { throws IOException {
byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s, byte[] confirm = crypto.deriveConfirmationRecord(s,
payloadEncoder.encode(theirPayload), payloadEncoder.encode(theirPayload),
payloadEncoder.encode(ourPayload), payloadEncoder.encode(ourPayload),
theirPublicKey, ourKeyPair, theirPublicKey, ourKeyPair,
@@ -167,10 +143,10 @@ class KeyAgreementProtocol {
transport.sendConfirm(confirm); transport.sendConfirm(confirm);
} }
private void receiveConfirm(SecretKey s, PublicKey theirPublicKey) private void receiveConfirm(SecretKey s, byte[] theirPublicKey)
throws AbortException { throws AbortException {
byte[] confirm = transport.receiveConfirm(); byte[] confirm = transport.receiveConfirm();
byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s, byte[] expected = crypto.deriveConfirmationRecord(s,
payloadEncoder.encode(theirPayload), payloadEncoder.encode(theirPayload),
payloadEncoder.encode(ourPayload), payloadEncoder.encode(ourPayload),
theirPublicKey, ourKeyPair, theirPublicKey, ourKeyPair,

View File

@@ -0,0 +1,46 @@
package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
import org.briarproject.bramble.api.keyagreement.KeyAgreementTaskFactory;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.system.Clock;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
@Immutable
@NotNullByDefault
class KeyAgreementTaskFactoryImpl implements KeyAgreementTaskFactory {
private final Clock clock;
private final CryptoComponent crypto;
private final EventBus eventBus;
private final Executor ioExecutor;
private final PayloadEncoder payloadEncoder;
private final PluginManager pluginManager;
@Inject
KeyAgreementTaskFactoryImpl(Clock clock, CryptoComponent crypto,
EventBus eventBus, @IoExecutor Executor ioExecutor,
PayloadEncoder payloadEncoder, PluginManager pluginManager) {
this.clock = clock;
this.crypto = crypto;
this.eventBus = eventBus;
this.ioExecutor = ioExecutor;
this.payloadEncoder = payloadEncoder;
this.pluginManager = pluginManager;
}
@Override
public KeyAgreementTask createTask() {
return new KeyAgreementTaskImpl(clock, crypto, eventBus, payloadEncoder,
pluginManager, ioExecutor);
}
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.keyagreement; package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -15,7 +14,6 @@ import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementListeningEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementStartedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent; import org.briarproject.bramble.api.keyagreement.event.KeyAgreementWaitingEvent;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager; import org.briarproject.bramble.api.plugin.PluginManager;
@@ -25,8 +23,6 @@ import java.io.IOException;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -39,7 +35,6 @@ class KeyAgreementTaskImpl extends Thread implements
Logger.getLogger(KeyAgreementTaskImpl.class.getName()); Logger.getLogger(KeyAgreementTaskImpl.class.getName());
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final KeyAgreementCrypto keyAgreementCrypto;
private final EventBus eventBus; private final EventBus eventBus;
private final PayloadEncoder payloadEncoder; private final PayloadEncoder payloadEncoder;
private final KeyPair localKeyPair; private final KeyPair localKeyPair;
@@ -48,17 +43,14 @@ class KeyAgreementTaskImpl extends Thread implements
private Payload localPayload; private Payload localPayload;
private Payload remotePayload; private Payload remotePayload;
@Inject
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto, KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus, EventBus eventBus, PayloadEncoder payloadEncoder,
PayloadEncoder payloadEncoder, PluginManager pluginManager, PluginManager pluginManager, Executor ioExecutor) {
@IoExecutor Executor ioExecutor) {
this.crypto = crypto; this.crypto = crypto;
this.keyAgreementCrypto = keyAgreementCrypto;
this.eventBus = eventBus; this.eventBus = eventBus;
this.payloadEncoder = payloadEncoder; this.payloadEncoder = payloadEncoder;
localKeyPair = crypto.generateAgreementKeyPair(); localKeyPair = crypto.generateAgreementKeyPair();
connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto, connector = new KeyAgreementConnector(this, clock, crypto,
pluginManager, ioExecutor); pluginManager, ioExecutor);
} }
@@ -108,8 +100,8 @@ class KeyAgreementTaskImpl extends Thread implements
// Run BQP protocol over the connection // Run BQP protocol over the connection
LOG.info("Starting BQP protocol"); LOG.info("Starting BQP protocol");
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto, KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
keyAgreementCrypto, payloadEncoder, transport, remotePayload, payloadEncoder, transport, remotePayload, localPayload,
localPayload, localKeyPair, alice); localKeyPair, alice);
try { try {
SecretKey master = protocol.perform(); SecretKey master = protocol.perform();
KeyAgreementResult result = KeyAgreementResult result =

View File

@@ -51,7 +51,8 @@ class PayloadParserImpl implements PayloadParser {
byte[] commitment = payload.getRaw(1); byte[] commitment = payload.getRaw(1);
if (commitment.length != COMMIT_LENGTH) throw new FormatException(); if (commitment.length != COMMIT_LENGTH) throw new FormatException();
// Remaining elements: transport descriptors // Remaining elements: transport descriptors
List<TransportDescriptor> recognised = new ArrayList<>(); List<TransportDescriptor> recognised =
new ArrayList<TransportDescriptor>();
for (int i = 2; i < payload.size(); i++) { for (int i = 2; i < payload.size(); i++) {
BdfList descriptor = payload.getList(i); BdfList descriptor = payload.getList(i);
long transportId = descriptor.getLong(0); long transportId = descriptor.getLong(0);

View File

@@ -63,9 +63,9 @@ class LifecycleManagerImpl implements LifecycleManager {
this.crypto = crypto; this.crypto = crypto;
this.authorFactory = authorFactory; this.authorFactory = authorFactory;
this.identityManager = identityManager; this.identityManager = identityManager;
services = new CopyOnWriteArrayList<>(); services = new CopyOnWriteArrayList<Service>();
clients = new CopyOnWriteArrayList<>(); clients = new CopyOnWriteArrayList<Client>();
executors = new CopyOnWriteArrayList<>(); executors = new CopyOnWriteArrayList<ExecutorService>();
} }
@Override @Override
@@ -88,7 +88,7 @@ class LifecycleManagerImpl implements LifecycleManager {
executors.add(e); executors.add(e);
} }
private LocalAuthor createLocalAuthor(String nickname) { private LocalAuthor createLocalAuthor(final String nickname) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
KeyPair keyPair = crypto.generateSignatureKeyPair(); KeyPair keyPair = crypto.generateSignatureKeyPair();
byte[] publicKey = keyPair.getPublic().getEncoded(); byte[] publicKey = keyPair.getPublic().getEncoded();
@@ -203,7 +203,9 @@ class LifecycleManagerImpl implements LifecycleManager {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Closing database took " + duration + " ms"); LOG.info("Closing database took " + duration + " ms");
shutdownLatch.countDown(); shutdownLatch.countDown();
} catch (DbException | ServiceException e) { } catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} catch (ServiceException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} finally { } finally {
startStopSemaphore.release(); startStopSemaphore.release();

View File

@@ -37,7 +37,7 @@ public class LifecycleModule {
public LifecycleModule() { public LifecycleModule() {
// The thread pool is unbounded, so use direct handoff // The thread pool is unbounded, so use direct handoff
BlockingQueue<Runnable> queue = new SynchronousQueue<>(); BlockingQueue<Runnable> queue = new SynchronousQueue<Runnable>();
// Discard tasks that are submitted during shutdown // Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy = RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy(); new ThreadPoolExecutor.DiscardPolicy();

View File

@@ -21,7 +21,7 @@ class ShutdownManagerImpl implements ShutdownManager {
private int nextHandle = 0; private int nextHandle = 0;
ShutdownManagerImpl() { ShutdownManagerImpl() {
hooks = new HashMap<>(); hooks = new HashMap<Integer, Thread>();
} }
@Override @Override

View File

@@ -134,7 +134,11 @@ class ConnectionManagerImpl implements ConnectionManager {
try { try {
byte[] tag = readTag(reader); byte[] tag = readTag(reader);
ctx = keyManager.getStreamContext(transportId, tag); ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false);
return;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false); disposeReader(true, false);
return; return;
@@ -245,7 +249,11 @@ class ConnectionManagerImpl implements ConnectionManager {
try { try {
byte[] tag = readTag(reader); byte[] tag = readTag(reader);
ctx = keyManager.getStreamContext(transportId, tag); ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false);
return;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false); disposeReader(true, false);
return; return;
@@ -258,7 +266,12 @@ class ConnectionManagerImpl implements ConnectionManager {
contactId = ctx.getContactId(); contactId = ctx.getContactId();
connectionRegistry.registerConnection(contactId, transportId, true); connectionRegistry.registerConnection(contactId, transportId, true);
// Start the outgoing session on another thread // Start the outgoing session on another thread
ioExecutor.execute(this::runOutgoingSession); ioExecutor.execute(new Runnable() {
@Override
public void run() {
runOutgoingSession();
}
});
try { try {
// Create and run the incoming session // Create and run the incoming session
incomingSession = createIncomingSession(ctx, reader); incomingSession = createIncomingSession(ctx, reader);
@@ -355,7 +368,12 @@ class ConnectionManagerImpl implements ConnectionManager {
return; return;
} }
// Start the incoming session on another thread // Start the incoming session on another thread
ioExecutor.execute(this::runIncomingSession); ioExecutor.execute(new Runnable() {
@Override
public void run() {
runIncomingSession();
}
});
try { try {
// Create and run the outgoing session // Create and run the outgoing session
outgoingSession = createDuplexOutgoingSession(ctx, writer); outgoingSession = createDuplexOutgoingSession(ctx, writer);
@@ -373,7 +391,11 @@ class ConnectionManagerImpl implements ConnectionManager {
try { try {
byte[] tag = readTag(reader); byte[] tag = readTag(reader);
ctx = keyManager.getStreamContext(transportId, tag); ctx = keyManager.getStreamContext(transportId, tag);
} catch (IOException | DbException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false);
return;
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
disposeReader(true, false); disposeReader(true, false);
return; return;

View File

@@ -42,8 +42,8 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
@Inject @Inject
ConnectionRegistryImpl(EventBus eventBus) { ConnectionRegistryImpl(EventBus eventBus) {
this.eventBus = eventBus; this.eventBus = eventBus;
connections = new HashMap<>(); connections = new HashMap<TransportId, Map<ContactId, Integer>>();
contactCounts = new HashMap<>(); contactCounts = new HashMap<ContactId, Integer>();
} }
@Override @Override
@@ -58,7 +58,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
try { try {
Map<ContactId, Integer> m = connections.get(t); Map<ContactId, Integer> m = connections.get(t);
if (m == null) { if (m == null) {
m = new HashMap<>(); m = new HashMap<ContactId, Integer>();
connections.put(t, m); connections.put(t, m);
} }
Integer count = m.get(c); Integer count = m.get(c);
@@ -124,7 +124,7 @@ class ConnectionRegistryImpl implements ConnectionRegistry {
try { try {
Map<ContactId, Integer> m = connections.get(t); Map<ContactId, Integer> m = connections.get(t);
if (m == null) return Collections.emptyList(); if (m == null) return Collections.emptyList();
List<ContactId> ids = new ArrayList<>(m.keySet()); List<ContactId> ids = new ArrayList<ContactId>(m.keySet());
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info(ids.size() + " contacts connected"); LOG.info(ids.size() + " contacts connected");
return ids; return ids;

View File

@@ -82,10 +82,10 @@ class PluginManagerImpl implements PluginManager, Service {
this.settingsManager = settingsManager; this.settingsManager = settingsManager;
this.transportPropertyManager = transportPropertyManager; this.transportPropertyManager = transportPropertyManager;
this.uiCallback = uiCallback; this.uiCallback = uiCallback;
plugins = new ConcurrentHashMap<>(); plugins = new ConcurrentHashMap<TransportId, Plugin>();
simplexPlugins = new CopyOnWriteArrayList<>(); simplexPlugins = new CopyOnWriteArrayList<SimplexPlugin>();
duplexPlugins = new CopyOnWriteArrayList<>(); duplexPlugins = new CopyOnWriteArrayList<DuplexPlugin>();
startLatches = new ConcurrentHashMap<>(); startLatches = new ConcurrentHashMap<TransportId, CountDownLatch>();
} }
@Override @Override
@@ -156,17 +156,17 @@ class PluginManagerImpl implements PluginManager, Service {
@Override @Override
public Collection<SimplexPlugin> getSimplexPlugins() { public Collection<SimplexPlugin> getSimplexPlugins() {
return new ArrayList<>(simplexPlugins); return new ArrayList<SimplexPlugin>(simplexPlugins);
} }
@Override @Override
public Collection<DuplexPlugin> getDuplexPlugins() { public Collection<DuplexPlugin> getDuplexPlugins() {
return new ArrayList<>(duplexPlugins); return new ArrayList<DuplexPlugin>(duplexPlugins);
} }
@Override @Override
public Collection<DuplexPlugin> getKeyAgreementPlugins() { public Collection<DuplexPlugin> getKeyAgreementPlugins() {
List<DuplexPlugin> supported = new ArrayList<>(); List<DuplexPlugin> supported = new ArrayList<DuplexPlugin>();
for (DuplexPlugin d : duplexPlugins) for (DuplexPlugin d : duplexPlugins)
if (d.supportsKeyAgreement()) supported.add(d); if (d.supportsKeyAgreement()) supported.add(d);
return supported; return supported;

View File

@@ -66,7 +66,7 @@ class Poller implements EventListener {
this.random = random; this.random = random;
this.clock = clock; this.clock = clock;
lock = new ReentrantLock(); lock = new ReentrantLock();
tasks = new HashMap<>(); tasks = new HashMap<TransportId, PollTask>();
} }
@Override @Override
@@ -111,24 +111,30 @@ class Poller implements EventListener {
connectToContact(c, (DuplexPlugin) p); connectToContact(c, (DuplexPlugin) p);
} }
private void connectToContact(ContactId c, SimplexPlugin p) { private void connectToContact(final ContactId c, final SimplexPlugin p) {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
TransportId t = p.getId(); @Override
if (!connectionRegistry.isConnected(c, t)) { public void run() {
TransportConnectionWriter w = p.createWriter(c); TransportId t = p.getId();
if (w != null) if (!connectionRegistry.isConnected(c, t)) {
connectionManager.manageOutgoingConnection(c, t, w); TransportConnectionWriter w = p.createWriter(c);
if (w != null)
connectionManager.manageOutgoingConnection(c, t, w);
}
} }
}); });
} }
private void connectToContact(ContactId c, DuplexPlugin p) { private void connectToContact(final ContactId c, final DuplexPlugin p) {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
TransportId t = p.getId(); @Override
if (!connectionRegistry.isConnected(c, t)) { public void run() {
DuplexTransportConnection d = p.createConnection(c); TransportId t = p.getId();
if (d != null) if (!connectionRegistry.isConnected(c, t)) {
connectionManager.manageOutgoingConnection(c, t, d); DuplexTransportConnection d = p.createConnection(c);
if (d != null)
connectionManager.manageOutgoingConnection(c, t, d);
}
} }
}); });
} }
@@ -153,10 +159,14 @@ class Poller implements EventListener {
try { try {
PollTask scheduled = tasks.get(t); PollTask scheduled = tasks.get(t);
if (scheduled == null || due < scheduled.due) { if (scheduled == null || due < scheduled.due) {
PollTask task = new PollTask(p, due, randomiseNext); final PollTask task = new PollTask(p, due, randomiseNext);
tasks.put(t, task); tasks.put(t, task);
scheduler.schedule( scheduler.schedule(new Runnable() {
() -> ioExecutor.execute(task), delay, MILLISECONDS); @Override
public void run() {
ioExecutor.execute(task);
}
}, delay, MILLISECONDS);
} }
} finally { } finally {
lock.unlock(); lock.unlock();
@@ -164,7 +174,7 @@ class Poller implements EventListener {
} }
@IoExecutor @IoExecutor
private void poll(Plugin p) { private void poll(final Plugin p) {
TransportId t = p.getId(); TransportId t = p.getId();
if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t); if (LOG.isLoggable(INFO)) LOG.info("Polling plugin " + t);
p.poll(connectionRegistry.getConnectedContacts(t)); p.poll(connectionRegistry.getConnectedContacts(t));

View File

@@ -108,7 +108,7 @@ abstract class FilePlugin implements SimplexPlugin {
} }
} }
protected void createReaderFromFile(File f) { protected void createReaderFromFile(final File f) {
if (!running) return; if (!running) return;
ioExecutor.execute(new ReaderCreator(f)); ioExecutor.execute(new ReaderCreator(f));
} }

View File

@@ -63,7 +63,7 @@ class LanTcpPlugin extends TcpPlugin {
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
String oldIpPorts = p.get(PROP_IP_PORTS); String oldIpPorts = p.get(PROP_IP_PORTS);
List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts); List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts);
List<InetSocketAddress> locals = new LinkedList<>(); List<InetSocketAddress> locals = new LinkedList<InetSocketAddress>();
for (InetAddress local : getLocalIpAddresses()) { for (InetAddress local : getLocalIpAddresses()) {
if (isAcceptableAddress(local)) { if (isAcceptableAddress(local)) {
// If this is the old address, try to use the same port // If this is the old address, try to use the same port
@@ -82,7 +82,7 @@ class LanTcpPlugin extends TcpPlugin {
private List<InetSocketAddress> parseSocketAddresses(String ipPorts) { private List<InetSocketAddress> parseSocketAddresses(String ipPorts) {
if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList(); if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
String[] split = ipPorts.split(SEPARATOR); String[] split = ipPorts.split(SEPARATOR);
List<InetSocketAddress> addresses = new ArrayList<>(); List<InetSocketAddress> addresses = new ArrayList<InetSocketAddress>();
for (String ipPort : split) { for (String ipPort : split) {
InetSocketAddress a = parseSocketAddress(ipPort); InetSocketAddress a = parseSocketAddress(ipPort);
if (a != null) addresses.add(a); if (a != null) addresses.add(a);
@@ -95,7 +95,7 @@ class LanTcpPlugin extends TcpPlugin {
String ipPort = getIpPortString(a); String ipPort = getIpPortString(a);
// Get the list of recently used addresses // Get the list of recently used addresses
String setting = callback.getSettings().get(PREF_LAN_IP_PORTS); String setting = callback.getSettings().get(PREF_LAN_IP_PORTS);
List<String> recent = new ArrayList<>(); List<String> recent = new ArrayList<String>();
if (!StringUtils.isNullOrEmpty(setting)) if (!StringUtils.isNullOrEmpty(setting))
Collections.addAll(recent, setting.split(SEPARATOR)); Collections.addAll(recent, setting.split(SEPARATOR));
// Is the address already in the list? // Is the address already in the list?
@@ -111,7 +111,7 @@ class LanTcpPlugin extends TcpPlugin {
recent = recent.subList(0, MAX_ADDRESSES); recent = recent.subList(0, MAX_ADDRESSES);
setting = StringUtils.join(recent, SEPARATOR); setting = StringUtils.join(recent, SEPARATOR);
// Update the list of addresses shared with contacts // Update the list of addresses shared with contacts
List<String> shared = new ArrayList<>(recent); List<String> shared = new ArrayList<String>(recent);
Collections.sort(shared); Collections.sort(shared);
String property = StringUtils.join(shared, SEPARATOR); String property = StringUtils.join(shared, SEPARATOR);
TransportProperties properties = new TransportProperties(); TransportProperties properties = new TransportProperties();
@@ -260,12 +260,16 @@ class LanTcpPlugin extends TcpPlugin {
@Override @Override
public Callable<KeyAgreementConnection> listen() { public Callable<KeyAgreementConnection> listen() {
return () -> { return new Callable<KeyAgreementConnection>() {
Socket s = ss.accept(); @Override
if (LOG.isLoggable(INFO)) public KeyAgreementConnection call() throws IOException {
LOG.info(ID.getString() + ": Incoming connection"); Socket s = ss.accept();
return new KeyAgreementConnection( if (LOG.isLoggable(INFO))
new TcpTransportConnection(LanTcpPlugin.this, s), ID); LOG.info(ID.getString() + ": Incoming connection");
return new KeyAgreementConnection(
new TcpTransportConnection(LanTcpPlugin.this, s),
ID);
}
}; };
} }

View File

@@ -37,7 +37,7 @@ class PortMapperImpl implements PortMapper {
} }
@Override @Override
public MappingResult map(int port) { public MappingResult map(final int port) {
if (!started.getAndSet(true)) start(); if (!started.getAndSet(true)) start();
if (gateway == null) return null; if (gateway == null) return null;
InetAddress internal = gateway.getLocalAddress(); InetAddress internal = gateway.getLocalAddress();
@@ -50,7 +50,12 @@ class PortMapperImpl implements PortMapper {
succeeded = gateway.addPortMapping(port, port, succeeded = gateway.addPortMapping(port, port,
getHostAddress(internal), "TCP", "TCP"); getHostAddress(internal), "TCP", "TCP");
if (succeeded) { if (succeeded) {
shutdownManager.addShutdownHook(() -> deleteMapping(port)); shutdownManager.addShutdownHook(new Runnable() {
@Override
public void run() {
deleteMapping(port);
}
});
} }
String externalString = gateway.getExternalIPAddress(); String externalString = gateway.getExternalIPAddress();
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -58,7 +63,9 @@ class PortMapperImpl implements PortMapper {
"External address " + scrubInetAddress(externalString)); "External address " + scrubInetAddress(externalString));
if (externalString != null) if (externalString != null)
external = InetAddress.getByName(externalString); external = InetAddress.getByName(externalString);
} catch (IOException | SAXException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} catch (SAXException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
return new MappingResult(internal, external, port, succeeded); return new MappingResult(internal, external, port, succeeded);
@@ -75,7 +82,11 @@ class PortMapperImpl implements PortMapper {
GatewayDiscover d = new GatewayDiscover(); GatewayDiscover d = new GatewayDiscover();
try { try {
d.discover(); d.discover();
} catch (IOException | SAXException | ParserConfigurationException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} catch (SAXException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} catch (ParserConfigurationException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
gateway = d.getValidGateway(); gateway = d.getValidGateway();
@@ -86,7 +97,9 @@ class PortMapperImpl implements PortMapper {
gateway.deletePortMapping(port, "TCP"); gateway.deletePortMapping(port, "TCP");
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.info("Deleted mapping for port " + port); LOG.info("Deleted mapping for port " + port);
} catch (IOException | SAXException e) { } catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} catch (SAXException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
} }

View File

@@ -110,37 +110,41 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
protected void bind() { protected void bind() {
ioExecutor.execute(() -> { ioExecutor.execute(new Runnable() {
if (!running) return; @Override
ServerSocket ss = null; public void run() {
for (InetSocketAddress addr : getLocalSocketAddresses()) { if (!running) return;
try { ServerSocket ss = null;
ss = new ServerSocket(); for (InetSocketAddress addr : getLocalSocketAddresses()) {
ss.bind(addr); try {
break; ss = new ServerSocket();
} catch (IOException e) { ss.bind(addr);
if (LOG.isLoggable(INFO)) break;
LOG.info("Failed to bind " + scrubSocketAddress(addr)); } catch (IOException e) {
tryToClose(ss); if (LOG.isLoggable(INFO))
LOG.info("Failed to bind " +
scrubSocketAddress(addr));
tryToClose(ss);
}
} }
if (ss == null || !ss.isBound()) {
LOG.info("Could not bind server socket");
return;
}
if (!running) {
tryToClose(ss);
return;
}
socket = ss;
backoff.reset();
InetSocketAddress local =
(InetSocketAddress) ss.getLocalSocketAddress();
setLocalSocketAddress(local);
if (LOG.isLoggable(INFO))
LOG.info("Listening on " + scrubSocketAddress(local));
callback.transportEnabled();
acceptContactConnections();
} }
if (ss == null || !ss.isBound()) {
LOG.info("Could not bind server socket");
return;
}
if (!running) {
tryToClose(ss);
return;
}
socket = ss;
backoff.reset();
InetSocketAddress local =
(InetSocketAddress) ss.getLocalSocketAddress();
setLocalSocketAddress(local);
if (LOG.isLoggable(INFO))
LOG.info("Listening on " + scrubSocketAddress(local));
callback.transportEnabled();
acceptContactConnections();
}); });
} }
@@ -214,13 +218,17 @@ abstract class TcpPlugin implements DuplexPlugin {
} }
} }
private void connectAndCallBack(ContactId c, TransportProperties p) { private void connectAndCallBack(final ContactId c,
ioExecutor.execute(() -> { final TransportProperties p) {
if (!isRunning()) return; ioExecutor.execute(new Runnable() {
DuplexTransportConnection d = createConnection(p); @Override
if (d != null) { public void run() {
backoff.reset(); if (!isRunning()) return;
callback.outgoingConnectionCreated(c, d); DuplexTransportConnection d = createConnection(p);
if (d != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, d);
}
} }
}); });
} }
@@ -309,7 +317,7 @@ abstract class TcpPlugin implements DuplexPlugin {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
return Collections.emptyList(); return Collections.emptyList();
} }
List<InetAddress> addrs = new ArrayList<>(); List<InetAddress> addrs = new ArrayList<InetAddress>();
for (NetworkInterface iface : ifaces) for (NetworkInterface iface : ifaces)
addrs.addAll(Collections.list(iface.getInetAddresses())); addrs.addAll(Collections.list(iface.getInetAddresses()));
return addrs; return addrs;

View File

@@ -43,7 +43,7 @@ class WanTcpPlugin extends TcpPlugin {
// Use the same address and port as last time if available // Use the same address and port as last time if available
TransportProperties p = callback.getLocalProperties(); TransportProperties p = callback.getLocalProperties();
InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT)); InetSocketAddress old = parseSocketAddress(p.get(PROP_IP_PORT));
List<InetSocketAddress> addrs = new LinkedList<>(); List<InetSocketAddress> addrs = new LinkedList<InetSocketAddress>();
for (InetAddress a : getLocalIpAddresses()) { for (InetAddress a : getLocalIpAddresses()) {
if (isAcceptableAddress(a)) { if (isAcceptableAddress(a)) {
// If this is the old address, try to use the same port // If this is the old address, try to use the same port

View File

@@ -40,12 +40,9 @@ public class PropertiesModule {
@Provides @Provides
@Singleton @Singleton
TransportPropertyManager getTransportPropertyManager( TransportPropertyManager getTransportPropertyManager(
LifecycleManager lifecycleManager, LifecycleManager lifecycleManager, ContactManager contactManager,
ValidationManager validationManager, ContactManager contactManager,
TransportPropertyManagerImpl transportPropertyManager) { TransportPropertyManagerImpl transportPropertyManager) {
lifecycleManager.registerClient(transportPropertyManager); lifecycleManager.registerClient(transportPropertyManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
transportPropertyManager);
contactManager.registerAddContactHook(transportPropertyManager); contactManager.registerAddContactHook(transportPropertyManager);
contactManager.registerRemoveContactHook(transportPropertyManager); contactManager.registerRemoveContactHook(transportPropertyManager);
return transportPropertyManager; return transportPropertyManager;

View File

@@ -9,10 +9,8 @@ import org.briarproject.bramble.api.contact.ContactManager.AddContactHook;
import org.briarproject.bramble.api.contact.ContactManager.RemoveContactHook; import org.briarproject.bramble.api.contact.ContactManager.RemoveContactHook;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfList; import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -21,10 +19,8 @@ import org.briarproject.bramble.api.properties.TransportPropertyManager;
import org.briarproject.bramble.api.sync.Client; import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.InvalidMessageException;
import org.briarproject.bramble.api.sync.Message; import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import java.util.HashMap; import java.util.HashMap;
@@ -40,26 +36,23 @@ import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class TransportPropertyManagerImpl implements TransportPropertyManager, class TransportPropertyManagerImpl implements TransportPropertyManager,
Client, AddContactHook, RemoveContactHook, IncomingMessageHook { Client, AddContactHook, RemoveContactHook {
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final MetadataParser metadataParser;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
private final Clock clock; private final Clock clock;
private final Group localGroup; private final Group localGroup;
@Inject @Inject
TransportPropertyManagerImpl(DatabaseComponent db, TransportPropertyManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, MetadataParser metadataParser, ClientHelper clientHelper, ContactGroupFactory contactGroupFactory,
ContactGroupFactory contactGroupFactory, Clock clock) { Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.metadataParser = metadataParser;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
this.clock = clock; this.clock = clock;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID, localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
CLIENT_VERSION);
} }
@Override @Override
@@ -91,31 +84,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
db.removeGroup(txn, getContactGroup(c)); db.removeGroup(txn, getContactGroup(c));
} }
@Override
public boolean incomingMessage(Transaction txn, Message m, Metadata meta)
throws DbException, InvalidMessageException {
try {
// Find the latest update for this transport, if any
BdfDictionary d = metadataParser.parse(meta);
TransportId t = new TransportId(d.getString("transportId"));
LatestUpdate latest = findLatest(txn, m.getGroupId(), t, false);
if (latest != null) {
if (d.getLong("version") > latest.version) {
// This update is newer - delete the previous update
db.deleteMessage(txn, latest.messageId);
db.deleteMessageMetadata(txn, latest.messageId);
} else {
// We've already received a newer update - delete this one
db.deleteMessage(txn, m.getId());
db.deleteMessageMetadata(txn, m.getId());
}
}
} catch (FormatException e) {
throw new InvalidMessageException(e);
}
return false;
}
@Override @Override
public void addRemoteProperties(Transaction txn, ContactId c, public void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException { Map<TransportId, TransportProperties> props) throws DbException {
@@ -144,9 +112,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
public Map<TransportId, TransportProperties> getLocalProperties( public Map<TransportId, TransportProperties> getLocalProperties(
Transaction txn) throws DbException { Transaction txn) throws DbException {
try { try {
Map<TransportId, TransportProperties> local = new HashMap<>(); Map<TransportId, TransportProperties> local =
new HashMap<TransportId, TransportProperties>();
// Find the latest local update for each transport // Find the latest local update for each transport
Map<TransportId, LatestUpdate> latest = findLatestLocal(txn); Map<TransportId, LatestUpdate> latest = findLatest(txn,
localGroup.getId(), true);
// Retrieve and parse the latest local properties // Retrieve and parse the latest local properties
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) { for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
BdfList message = clientHelper.getMessageAsList(txn, BdfList message = clientHelper.getMessageAsList(txn,
@@ -190,7 +160,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public Map<ContactId, TransportProperties> getRemoteProperties( public Map<ContactId, TransportProperties> getRemoteProperties(
TransportId t) throws DbException { TransportId t) throws DbException {
Map<ContactId, TransportProperties> remote = new HashMap<>(); Map<ContactId, TransportProperties> remote =
new HashMap<ContactId, TransportProperties>();
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
for (Contact c : db.getContacts(txn)) for (Contact c : db.getContacts(txn))
@@ -263,9 +234,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
long version = latest == null ? 1 : latest.version + 1; long version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, localGroup.getId(), t, merged, version, storeMessage(txn, localGroup.getId(), t, merged, version,
true, false); true, false);
// Delete the previous update, if any
if (latest != null)
db.removeMessage(txn, latest.messageId);
// Store the merged properties in each contact's group // Store the merged properties in each contact's group
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
Group g = getContactGroup(c); Group g = getContactGroup(c);
@@ -273,9 +241,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
version = latest == null ? 1 : latest.version + 1; version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, g.getId(), t, merged, version, storeMessage(txn, g.getId(), t, merged, version,
true, true); true, true);
// Delete the previous update, if any
if (latest != null)
db.removeMessage(txn, latest.messageId);
} }
} }
db.commitTransaction(txn); db.commitTransaction(txn);
@@ -288,8 +253,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
private Group getContactGroup(Contact c) { private Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID, c);
CLIENT_VERSION, c);
} }
private void storeMessage(Transaction txn, GroupId g, TransportId t, private void storeMessage(Transaction txn, GroupId g, TransportId t,
@@ -314,16 +278,21 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
return BdfList.of(t.getString(), version, p); return BdfList.of(t.getString(), version, p);
} }
private Map<TransportId, LatestUpdate> findLatestLocal(Transaction txn) private Map<TransportId, LatestUpdate> findLatest(Transaction txn,
throws DbException, FormatException { GroupId g, boolean local) throws DbException, FormatException {
Map<TransportId, LatestUpdate> latestUpdates = new HashMap<>(); Map<TransportId, LatestUpdate> latestUpdates =
Map<MessageId, BdfDictionary> metadata = clientHelper new HashMap<TransportId, LatestUpdate>();
.getMessageMetadataAsDictionary(txn, localGroup.getId()); Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g);
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) { for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue(); BdfDictionary meta = e.getValue();
TransportId t = new TransportId(meta.getString("transportId")); if (meta.getBoolean("local") == local) {
long version = meta.getLong("version"); TransportId t = new TransportId(meta.getString("transportId"));
latestUpdates.put(t, new LatestUpdate(e.getKey(), version)); long version = meta.getLong("version");
LatestUpdate latest = latestUpdates.get(t);
if (latest == null || version > latest.version)
latestUpdates.put(t, new LatestUpdate(e.getKey(), version));
}
} }
return latestUpdates; return latestUpdates;
} }
@@ -331,16 +300,19 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Nullable @Nullable
private LatestUpdate findLatest(Transaction txn, GroupId g, TransportId t, private LatestUpdate findLatest(Transaction txn, GroupId g, TransportId t,
boolean local) throws DbException, FormatException { boolean local) throws DbException, FormatException {
LatestUpdate latest = null;
Map<MessageId, BdfDictionary> metadata = Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g); clientHelper.getMessageMetadataAsDictionary(txn, g);
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) { for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue(); BdfDictionary meta = e.getValue();
if (meta.getString("transportId").equals(t.getString()) if (meta.getString("transportId").equals(t.getString())
&& meta.getBoolean("local") == local) { && meta.getBoolean("local") == local) {
return new LatestUpdate(e.getKey(), meta.getLong("version")); long version = meta.getLong("version");
if (latest == null || version > latest.version)
latest = new LatestUpdate(e.getKey(), version);
} }
} }
return null; return latest;
} }
private TransportProperties parseProperties(BdfList message) private TransportProperties parseProperties(BdfList message)

View File

@@ -41,7 +41,7 @@ class Receiver implements ReadHandler {
Receiver(Clock clock, Sender sender) { Receiver(Clock clock, Sender sender) {
this.sender = sender; this.sender = sender;
this.clock = clock; this.clock = clock;
dataFrames = new TreeSet<>(new SequenceNumberComparator()); dataFrames = new TreeSet<Data>(new SequenceNumberComparator());
} }
Data read() throws IOException, InterruptedException { Data read() throws IOException, InterruptedException {

View File

@@ -42,44 +42,48 @@ class ReliabilityLayerImpl implements ReliabilityLayer, WriteHandler {
this.executor = executor; this.executor = executor;
this.clock = clock; this.clock = clock;
this.writeHandler = writeHandler; this.writeHandler = writeHandler;
writes = new LinkedBlockingQueue<>(); writes = new LinkedBlockingQueue<byte[]>();
} }
@Override @Override
public void start() { public void start() {
SlipEncoder encoder = new SlipEncoder(this); SlipEncoder encoder = new SlipEncoder(this);
Sender sender = new Sender(clock, encoder); final Sender sender = new Sender(clock, encoder);
receiver = new Receiver(clock, sender); receiver = new Receiver(clock, sender);
decoder = new SlipDecoder(receiver, Data.MAX_LENGTH); decoder = new SlipDecoder(receiver, Data.MAX_LENGTH);
inputStream = new ReceiverInputStream(receiver); inputStream = new ReceiverInputStream(receiver);
outputStream = new SenderOutputStream(sender); outputStream = new SenderOutputStream(sender);
running = true; running = true;
executor.execute(() -> { executor.execute(new Runnable() {
long now = clock.currentTimeMillis(); @Override
long next = now + TICK_INTERVAL; public void run() {
try { long now = clock.currentTimeMillis();
while (running) { long next = now + TICK_INTERVAL;
byte[] b = null; try {
while (now < next && b == null) { while (running) {
b = writes.poll(next - now, MILLISECONDS); byte[] b = null;
if (!running) return; while (now < next && b == null) {
now = clock.currentTimeMillis(); b = writes.poll(next - now, MILLISECONDS);
} if (!running) return;
if (b == null) { now = clock.currentTimeMillis();
sender.tick(); }
while (next <= now) next += TICK_INTERVAL; if (b == null) {
} else { sender.tick();
if (b.length == 0) return; // Poison pill while (next <= now) next += TICK_INTERVAL;
writeHandler.handleWrite(b); } else {
if (b.length == 0) return; // Poison pill
writeHandler.handleWrite(b);
}
} }
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting to write");
Thread.currentThread().interrupt();
running = false;
} catch (IOException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
running = false;
} }
} catch (InterruptedException e) {
LOG.warning("Interrupted while waiting to write");
Thread.currentThread().interrupt();
running = false;
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
running = false;
} }
}); });
} }

View File

@@ -46,7 +46,7 @@ class Sender {
Sender(Clock clock, WriteHandler writeHandler) { Sender(Clock clock, WriteHandler writeHandler) {
this.clock = clock; this.clock = clock;
this.writeHandler = writeHandler; this.writeHandler = writeHandler;
outstanding = new LinkedList<>(); outstanding = new LinkedList<Outstanding>();
} }
void sendAck(long sequenceNumber, int windowSize) throws IOException { void sendAck(long sequenceNumber, int windowSize) throws IOException {
@@ -136,7 +136,7 @@ class Sender {
if (now - o.lastTransmitted > rto) { if (now - o.lastTransmitted > rto) {
it.remove(); it.remove();
if (retransmit == null) if (retransmit == null)
retransmit = new ArrayList<>(); retransmit = new ArrayList<Outstanding>();
retransmit.add(o); retransmit.add(o);
// Update the retransmission timeout // Update the retransmission timeout
rto <<= 1; rto <<= 1;

View File

@@ -54,7 +54,12 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(DuplexOutgoingSession.class.getName()); Logger.getLogger(DuplexOutgoingSession.class.getName());
private static final ThrowingRunnable<IOException> CLOSE = () -> {}; private static final ThrowingRunnable<IOException> CLOSE =
new ThrowingRunnable<IOException>() {
@Override
public void run() {
}
};
private final DatabaseComponent db; private final DatabaseComponent db;
private final Executor dbExecutor; private final Executor dbExecutor;
@@ -78,7 +83,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
this.recordWriter = recordWriter; this.recordWriter = recordWriter;
writerTasks = new LinkedBlockingQueue<>(); writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
} }
@IoExecutor @IoExecutor

View File

@@ -6,16 +6,11 @@ import org.briarproject.bramble.api.sync.ClientId;
import org.briarproject.bramble.api.sync.Group; import org.briarproject.bramble.api.sync.Group;
import org.briarproject.bramble.api.sync.GroupFactory; import org.briarproject.bramble.api.sync.GroupFactory;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.GroupId.LABEL;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
class GroupFactoryImpl implements GroupFactory { class GroupFactoryImpl implements GroupFactory {
@@ -28,12 +23,9 @@ class GroupFactoryImpl implements GroupFactory {
} }
@Override @Override
public Group createGroup(ClientId c, int clientVersion, byte[] descriptor) { public Group createGroup(ClientId c, byte[] descriptor) {
byte[] clientVersionBytes = new byte[INT_32_BYTES]; byte[] hash = crypto.hash(GroupId.LABEL,
ByteUtils.writeUint32(clientVersion, clientVersionBytes, 0); StringUtils.toUtf8(c.getString()), descriptor);
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
StringUtils.toUtf8(c.getString()), clientVersionBytes,
descriptor);
return new Group(new GroupId(hash), c, descriptor); return new Group(new GroupId(hash), c, descriptor);
} }
} }

View File

@@ -12,10 +12,8 @@ import org.briarproject.bramble.util.ByteUtils;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.MessageId.LABEL;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
@@ -34,9 +32,9 @@ class MessageFactoryImpl implements MessageFactory {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
byte[] timeBytes = new byte[ByteUtils.INT_64_BYTES]; byte[] timeBytes = new byte[ByteUtils.INT_64_BYTES];
ByteUtils.writeUint64(timestamp, timeBytes, 0); ByteUtils.writeUint64(timestamp, timeBytes, 0);
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION}, byte[] idHash =
g.getBytes(), timeBytes, body); crypto.hash(MessageId.LABEL, g.getBytes(), timeBytes, body);
MessageId id = new MessageId(hash); MessageId id = new MessageId(idHash);
byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length]; byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH); System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH);
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH); ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);

View File

@@ -116,7 +116,7 @@ class RecordReaderImpl implements RecordReader {
private List<MessageId> readMessageIds() throws IOException { private List<MessageId> readMessageIds() throws IOException {
if (payloadLength == 0) throw new FormatException(); if (payloadLength == 0) throw new FormatException();
if (payloadLength % UniqueId.LENGTH != 0) throw new FormatException(); if (payloadLength % UniqueId.LENGTH != 0) throw new FormatException();
List<MessageId> ids = new ArrayList<>(); List<MessageId> ids = new ArrayList<MessageId>();
for (int off = 0; off < payloadLength; off += UniqueId.LENGTH) { for (int off = 0; off < payloadLength; off += UniqueId.LENGTH) {
byte[] id = new byte[UniqueId.LENGTH]; byte[] id = new byte[UniqueId.LENGTH];
System.arraycopy(payload, off, id, 0, UniqueId.LENGTH); System.arraycopy(payload, off, id, 0, UniqueId.LENGTH);

View File

@@ -43,7 +43,12 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(SimplexOutgoingSession.class.getName()); Logger.getLogger(SimplexOutgoingSession.class.getName());
private static final ThrowingRunnable<IOException> CLOSE = () -> {}; private static final ThrowingRunnable<IOException> CLOSE =
new ThrowingRunnable<IOException>() {
@Override
public void run() {
}
};
private final DatabaseComponent db; private final DatabaseComponent db;
private final Executor dbExecutor; private final Executor dbExecutor;
@@ -66,7 +71,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
this.maxLatency = maxLatency; this.maxLatency = maxLatency;
this.recordWriter = recordWriter; this.recordWriter = recordWriter;
outstandingQueries = new AtomicInteger(2); // One per type of record outstandingQueries = new AtomicInteger(2); // One per type of record
writerTasks = new LinkedBlockingQueue<>(); writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
} }
@IoExecutor @IoExecutor

View File

@@ -64,8 +64,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.validationExecutor = validationExecutor; this.validationExecutor = validationExecutor;
this.messageFactory = messageFactory; this.messageFactory = messageFactory;
validators = new ConcurrentHashMap<>(); validators = new ConcurrentHashMap<ClientId, MessageValidator>();
hooks = new ConcurrentHashMap<>(); hooks = new ConcurrentHashMap<ClientId, IncomingMessageHook>();
} }
@Override @Override
@@ -93,14 +93,19 @@ class ValidationManagerImpl implements ValidationManager, Service,
hooks.put(c, hook); hooks.put(c, hook);
} }
private void validateOutstandingMessagesAsync(ClientId c) { private void validateOutstandingMessagesAsync(final ClientId c) {
dbExecutor.execute(() -> validateOutstandingMessages(c)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
validateOutstandingMessages(c);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
private void validateOutstandingMessages(ClientId c) { private void validateOutstandingMessages(ClientId c) {
try { try {
Queue<MessageId> unvalidated = new LinkedList<>(); Queue<MessageId> unvalidated = new LinkedList<MessageId>();
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
unvalidated.addAll(db.getMessagesToValidate(txn, c)); unvalidated.addAll(db.getMessagesToValidate(txn, c));
@@ -114,9 +119,14 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
} }
private void validateNextMessageAsync(Queue<MessageId> unvalidated) { private void validateNextMessageAsync(final Queue<MessageId> unvalidated) {
if (unvalidated.isEmpty()) return; if (unvalidated.isEmpty()) return;
dbExecutor.execute(() -> validateNextMessage(unvalidated)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
validateNextMessage(unvalidated);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
@@ -148,14 +158,19 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
} }
private void deliverOutstandingMessagesAsync(ClientId c) { private void deliverOutstandingMessagesAsync(final ClientId c) {
dbExecutor.execute(() -> deliverOutstandingMessages(c)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
deliverOutstandingMessages(c);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
private void deliverOutstandingMessages(ClientId c) { private void deliverOutstandingMessages(ClientId c) {
try { try {
Queue<MessageId> pending = new LinkedList<>(); Queue<MessageId> pending = new LinkedList<MessageId>();
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
pending.addAll(db.getPendingMessages(txn, c)); pending.addAll(db.getPendingMessages(txn, c));
@@ -169,9 +184,15 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
} }
private void deliverNextPendingMessageAsync(Queue<MessageId> pending) { private void deliverNextPendingMessageAsync(
final Queue<MessageId> pending) {
if (pending.isEmpty()) return; if (pending.isEmpty()) return;
dbExecutor.execute(() -> deliverNextPendingMessage(pending)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
deliverNextPendingMessage(pending);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
@@ -208,7 +229,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
pending.addAll(getPendingDependents(txn, id)); pending.addAll(getPendingDependents(txn, id));
if (result.share) { if (result.share) {
db.setMessageShared(txn, id); db.setMessageShared(txn, id);
toShare = new LinkedList<>(states.keySet()); toShare = new LinkedList<MessageId>(
states.keySet());
} }
} else { } else {
invalidate = getDependentsToInvalidate(txn, id); invalidate = getDependentsToInvalidate(txn, id);
@@ -233,8 +255,13 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
} }
private void validateMessageAsync(Message m, Group g) { private void validateMessageAsync(final Message m, final Group g) {
validationExecutor.execute(() -> validateMessage(m, g)); validationExecutor.execute(new Runnable() {
@Override
public void run() {
validateMessage(m, g);
}
});
} }
@ValidationExecutor @ValidationExecutor
@@ -250,16 +277,21 @@ class ValidationManagerImpl implements ValidationManager, Service,
} catch (InvalidMessageException e) { } catch (InvalidMessageException e) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
LOG.log(INFO, e.toString(), e); LOG.log(INFO, e.toString(), e);
Queue<MessageId> invalidate = new LinkedList<>(); Queue<MessageId> invalidate = new LinkedList<MessageId>();
invalidate.add(m.getId()); invalidate.add(m.getId());
invalidateNextMessageAsync(invalidate); invalidateNextMessageAsync(invalidate);
} }
} }
} }
private void storeMessageContextAsync(Message m, ClientId c, private void storeMessageContextAsync(final Message m, final ClientId c,
MessageContext result) { final MessageContext result) {
dbExecutor.execute(() -> storeMessageContext(m, c, result)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
storeMessageContext(m, c, result);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
@@ -299,7 +331,8 @@ class ValidationManagerImpl implements ValidationManager, Service,
pending = getPendingDependents(txn, id); pending = getPendingDependents(txn, id);
if (result.share) { if (result.share) {
db.setMessageShared(txn, id); db.setMessageShared(txn, id);
toShare = new LinkedList<>(dependencies); toShare =
new LinkedList<MessageId>(dependencies);
} }
} else { } else {
invalidate = getDependentsToInvalidate(txn, id); invalidate = getDependentsToInvalidate(txn, id);
@@ -345,7 +378,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
@DatabaseExecutor @DatabaseExecutor
private Queue<MessageId> getPendingDependents(Transaction txn, MessageId m) private Queue<MessageId> getPendingDependents(Transaction txn, MessageId m)
throws DbException { throws DbException {
Queue<MessageId> pending = new LinkedList<>(); Queue<MessageId> pending = new LinkedList<MessageId>();
Map<MessageId, State> states = db.getMessageDependents(txn, m); Map<MessageId, State> states = db.getMessageDependents(txn, m);
for (Entry<MessageId, State> e : states.entrySet()) { for (Entry<MessageId, State> e : states.entrySet()) {
if (e.getValue() == PENDING) pending.add(e.getKey()); if (e.getValue() == PENDING) pending.add(e.getKey());
@@ -353,14 +386,19 @@ class ValidationManagerImpl implements ValidationManager, Service,
return pending; return pending;
} }
private void shareOutstandingMessagesAsync(ClientId c) { private void shareOutstandingMessagesAsync(final ClientId c) {
dbExecutor.execute(() -> shareOutstandingMessages(c)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
shareOutstandingMessages(c);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
private void shareOutstandingMessages(ClientId c) { private void shareOutstandingMessages(ClientId c) {
try { try {
Queue<MessageId> toShare = new LinkedList<>(); Queue<MessageId> toShare = new LinkedList<MessageId>();
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);
try { try {
toShare.addAll(db.getMessagesToShare(txn, c)); toShare.addAll(db.getMessagesToShare(txn, c));
@@ -380,9 +418,14 @@ class ValidationManagerImpl implements ValidationManager, Service,
* This method should only be called for messages that have all their * This method should only be called for messages that have all their
* dependencies delivered and have been delivered themselves. * dependencies delivered and have been delivered themselves.
*/ */
private void shareNextMessageAsync(Queue<MessageId> toShare) { private void shareNextMessageAsync(final Queue<MessageId> toShare) {
if (toShare.isEmpty()) return; if (toShare.isEmpty()) return;
dbExecutor.execute(() -> shareNextMessage(toShare)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
shareNextMessage(toShare);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
@@ -409,9 +452,14 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
} }
private void invalidateNextMessageAsync(Queue<MessageId> invalidate) { private void invalidateNextMessageAsync(final Queue<MessageId> invalidate) {
if (invalidate.isEmpty()) return; if (invalidate.isEmpty()) return;
dbExecutor.execute(() -> invalidateNextMessage(invalidate)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
invalidateNextMessage(invalidate);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
@@ -448,7 +496,7 @@ class ValidationManagerImpl implements ValidationManager, Service,
@DatabaseExecutor @DatabaseExecutor
private Queue<MessageId> getDependentsToInvalidate(Transaction txn, private Queue<MessageId> getDependentsToInvalidate(Transaction txn,
MessageId m) throws DbException { MessageId m) throws DbException {
Queue<MessageId> invalidate = new LinkedList<>(); Queue<MessageId> invalidate = new LinkedList<MessageId>();
Map<MessageId, State> states = db.getMessageDependents(txn, m); Map<MessageId, State> states = db.getMessageDependents(txn, m);
for (Entry<MessageId, State> e : states.entrySet()) { for (Entry<MessageId, State> e : states.entrySet()) {
if (e.getValue() != INVALID) invalidate.add(e.getKey()); if (e.getValue() != INVALID) invalidate.add(e.getKey());
@@ -466,12 +514,17 @@ class ValidationManagerImpl implements ValidationManager, Service,
} }
} }
private void loadGroupAndValidateAsync(Message m) { private void loadGroupAndValidateAsync(final Message m) {
dbExecutor.execute(() -> loadGroupAndValidate(m)); dbExecutor.execute(new Runnable() {
@Override
public void run() {
loadGroupAndValidate(m);
}
});
} }
@DatabaseExecutor @DatabaseExecutor
private void loadGroupAndValidate(Message m) { private void loadGroupAndValidate(final Message m) {
try { try {
Group g; Group g;
Transaction txn = db.startTransaction(true); Transaction txn = db.startTransaction(true);

View File

@@ -58,14 +58,15 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.transportKeyManagerFactory = transportKeyManagerFactory; this.transportKeyManagerFactory = transportKeyManagerFactory;
// Use a ConcurrentHashMap as a thread-safe set // Use a ConcurrentHashMap as a thread-safe set
activeContacts = new ConcurrentHashMap<>(); activeContacts = new ConcurrentHashMap<ContactId, Boolean>();
managers = new ConcurrentHashMap<>(); managers = new ConcurrentHashMap<TransportId, TransportKeyManager>();
} }
@Override @Override
public void startService() throws ServiceException { public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException(); if (used.getAndSet(true)) throw new IllegalStateException();
Map<TransportId, Integer> transports = new HashMap<>(); Map<TransportId, Integer> transports =
new HashMap<TransportId, Integer>();
for (SimplexPluginFactory f : pluginConfig.getSimplexFactories()) for (SimplexPluginFactory f : pluginConfig.getSimplexFactories())
transports.put(f.getId(), f.getMaxLatency()); transports.put(f.getId(), f.getMaxLatency());
for (DuplexPluginFactory f : pluginConfig.getDuplexFactories()) for (DuplexPluginFactory f : pluginConfig.getDuplexFactories())
@@ -155,10 +156,14 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
} }
private void removeContact(ContactId c) { private void removeContact(final ContactId c) {
activeContacts.remove(c); activeContacts.remove(c);
dbExecutor.execute(() -> { dbExecutor.execute(new Runnable() {
for (TransportKeyManager m : managers.values()) m.removeContact(c); @Override
public void run() {
for (TransportKeyManager m : managers.values())
m.removeContact(c);
}
}); });
} }
} }

View File

@@ -45,7 +45,7 @@ class ReorderingWindow {
} }
List<Long> getUnseen() { List<Long> getUnseen() {
List<Long> unseen = new ArrayList<>(seen.length); List<Long> unseen = new ArrayList<Long>(seen.length);
for (int i = 0; i < seen.length; i++) for (int i = 0; i < seen.length; i++)
if (!seen[i]) unseen.add(base + i); if (!seen[i]) unseen.add(base + i);
return unseen; return unseen;
@@ -69,8 +69,8 @@ class ReorderingWindow {
return new Change(added, removed); return new Change(added, removed);
} }
// Record the elements that will be added and removed // Record the elements that will be added and removed
List<Long> added = new ArrayList<>(slide); List<Long> added = new ArrayList<Long>(slide);
List<Long> removed = new ArrayList<>(slide); List<Long> removed = new ArrayList<Long>(slide);
for (int i = 0; i < slide; i++) { for (int i = 0; i < slide; i++) {
if (!seen[i]) removed.add(base + i); if (!seen[i]) removed.add(base + i);
added.add(base + seen.length + i); added.add(base + seen.length + i);

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -20,18 +20,17 @@ class TransportKeyManagerFactoryImpl implements
TransportKeyManagerFactory { TransportKeyManagerFactory {
private final DatabaseComponent db; private final DatabaseComponent db;
private final TransportCrypto transportCrypto; private final CryptoComponent crypto;
private final Executor dbExecutor; private final Executor dbExecutor;
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Clock clock; private final Clock clock;
@Inject @Inject
TransportKeyManagerFactoryImpl(DatabaseComponent db, TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto,
TransportCrypto transportCrypto,
@DatabaseExecutor Executor dbExecutor, @DatabaseExecutor Executor dbExecutor,
@Scheduler ScheduledExecutorService scheduler, Clock clock) { @Scheduler ScheduledExecutorService scheduler, Clock clock) {
this.db = db; this.db = db;
this.transportCrypto = transportCrypto; this.crypto = crypto;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.clock = clock; this.clock = clock;
@@ -40,8 +39,8 @@ class TransportKeyManagerFactoryImpl implements
@Override @Override
public TransportKeyManager createTransportKeyManager( public TransportKeyManager createTransportKeyManager(
TransportId transportId, long maxLatency) { TransportId transportId, long maxLatency) {
return new TransportKeyManagerImpl(db, transportCrypto, dbExecutor, return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler,
scheduler, clock, transportId, maxLatency); clock, transportId, maxLatency);
} }
} }

View File

@@ -2,8 +2,8 @@ package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.Bytes; import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
@@ -41,7 +41,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
Logger.getLogger(TransportKeyManagerImpl.class.getName()); Logger.getLogger(TransportKeyManagerImpl.class.getName());
private final DatabaseComponent db; private final DatabaseComponent db;
private final TransportCrypto transportCrypto; private final CryptoComponent crypto;
private final Executor dbExecutor; private final Executor dbExecutor;
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Clock clock; private final Clock clock;
@@ -54,21 +54,20 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final Map<ContactId, MutableOutgoingKeys> outContexts; private final Map<ContactId, MutableOutgoingKeys> outContexts;
private final Map<ContactId, MutableTransportKeys> keys; private final Map<ContactId, MutableTransportKeys> keys;
TransportKeyManagerImpl(DatabaseComponent db, TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
TransportCrypto transportCrypto, Executor dbExecutor, Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler,
@Scheduler ScheduledExecutorService scheduler, Clock clock, Clock clock, TransportId transportId, long maxLatency) {
TransportId transportId, long maxLatency) {
this.db = db; this.db = db;
this.transportCrypto = transportCrypto; this.crypto = crypto;
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.scheduler = scheduler; this.scheduler = scheduler;
this.clock = clock; this.clock = clock;
this.transportId = transportId; this.transportId = transportId;
rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
lock = new ReentrantLock(); lock = new ReentrantLock();
inContexts = new HashMap<>(); inContexts = new HashMap<Bytes, TagContext>();
outContexts = new HashMap<>(); outContexts = new HashMap<ContactId, MutableOutgoingKeys>();
keys = new HashMap<>(); keys = new HashMap<ContactId, MutableTransportKeys>();
} }
@Override @Override
@@ -100,8 +99,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) { for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
ContactId c = e.getKey(); ContactId c = e.getKey();
TransportKeys k = e.getValue(); TransportKeys k = e.getValue();
TransportKeys k1 = TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
transportCrypto.rotateTransportKeys(k, rotationPeriod);
if (k1.getRotationPeriod() > k.getRotationPeriod()) if (k1.getRotationPeriod() > k.getRotationPeriod())
rotationResult.rotated.put(c, k1); rotationResult.rotated.put(c, k1);
rotationResult.current.put(c, k1); rotationResult.current.put(c, k1);
@@ -129,29 +127,39 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : inKeys.getWindow().getUnseen()) { for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = new TagContext(c, inKeys, streamNumber); TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION, crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber); streamNumber);
inContexts.put(new Bytes(tag), tagCtx); inContexts.put(new Bytes(tag), tagCtx);
} }
} }
private void scheduleKeyRotation(long now) { private void scheduleKeyRotation(long now) {
Runnable task = new Runnable() {
@Override
public void run() {
rotateKeys();
}
};
long delay = rotationPeriodLength - now % rotationPeriodLength; long delay = rotationPeriodLength - now % rotationPeriodLength;
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS); scheduler.schedule(task, delay, MILLISECONDS);
} }
private void rotateKeys() { private void rotateKeys() {
dbExecutor.execute(() -> { dbExecutor.execute(new Runnable() {
try { @Override
Transaction txn = db.startTransaction(false); public void run() {
try { try {
rotateKeys(txn); Transaction txn = db.startTransaction(false);
db.commitTransaction(txn); try {
} finally { rotateKeys(txn);
db.endTransaction(txn); db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
} }
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
} }
}); });
} }
@@ -164,11 +172,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Work out what rotation period the timestamp belongs to // Work out what rotation period the timestamp belongs to
long rotationPeriod = timestamp / rotationPeriodLength; long rotationPeriod = timestamp / rotationPeriodLength;
// Derive the transport keys // Derive the transport keys
TransportKeys k = transportCrypto.deriveTransportKeys(transportId, TransportKeys k = crypto.deriveTransportKeys(transportId, master,
master, rotationPeriod, alice); rotationPeriod, alice);
// Rotate the keys to the current rotation period if necessary // Rotate the keys to the current rotation period if necessary
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength; rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
k = transportCrypto.rotateTransportKeys(k, rotationPeriod); k = crypto.rotateTransportKeys(k, rotationPeriod);
// Initialise mutable state for the contact // Initialise mutable state for the contact
addKeys(c, new MutableTransportKeys(k)); addKeys(c, new MutableTransportKeys(k));
// Write the keys back to the DB // Write the keys back to the DB
@@ -236,8 +244,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Add tags for any stream numbers added to the window // Add tags for any stream numbers added to the window
for (long streamNumber : change.getAdded()) { for (long streamNumber : change.getAdded()) {
byte[] addTag = new byte[TAG_LENGTH]; byte[] addTag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(addTag, inKeys.getTagKey(), crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
PROTOCOL_VERSION, streamNumber); streamNumber);
inContexts.put(new Bytes(addTag), new TagContext( inContexts.put(new Bytes(addTag), new TagContext(
tagCtx.contactId, inKeys, streamNumber)); tagCtx.contactId, inKeys, streamNumber));
} }
@@ -245,7 +253,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : change.getRemoved()) { for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue; if (streamNumber == tagCtx.streamNumber) continue;
byte[] removeTag = new byte[TAG_LENGTH]; byte[] removeTag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(), crypto.encodeTag(removeTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber); PROTOCOL_VERSION, streamNumber);
inContexts.remove(new Bytes(removeTag)); inContexts.remove(new Bytes(removeTag));
} }
@@ -264,7 +272,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
lock.lock(); lock.lock();
try { try {
// Rotate the keys to the current rotation period // Rotate the keys to the current rotation period
Map<ContactId, TransportKeys> snapshot = new HashMap<>(); Map<ContactId, TransportKeys> snapshot =
new HashMap<ContactId, TransportKeys>();
for (Entry<ContactId, MutableTransportKeys> e : keys.entrySet()) for (Entry<ContactId, MutableTransportKeys> e : keys.entrySet())
snapshot.put(e.getKey(), e.getValue().snapshot()); snapshot.put(e.getKey(), e.getValue().snapshot());
RotationResult rotationResult = rotateKeys(snapshot, now); RotationResult rotationResult = rotateKeys(snapshot, now);
@@ -302,8 +311,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final Map<ContactId, TransportKeys> current, rotated; private final Map<ContactId, TransportKeys> current, rotated;
private RotationResult() { private RotationResult() {
current = new HashMap<>(); current = new HashMap<ContactId, TransportKeys>();
rotated = new HashMap<>(); rotated = new HashMap<ContactId, TransportKeys>();
} }
} }
} }

View File

@@ -24,13 +24,16 @@ public class PoliteExecutorTest extends BrambleTestCase {
Executor delegate = Executors.newSingleThreadExecutor(); Executor delegate = Executors.newSingleThreadExecutor();
// Allow all the tasks to be delegated straight away // Allow all the tasks to be delegated straight away
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2); PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2);
List<Integer> list = new Vector<>(); final List<Integer> list = new Vector<Integer>();
CountDownLatch latch = new CountDownLatch(TASKS); final CountDownLatch latch = new CountDownLatch(TASKS);
for (int i = 0; i < TASKS; i++) { for (int i = 0; i < TASKS; i++) {
int result = i; final int result = i;
polite.execute(() -> { polite.execute(new Runnable() {
list.add(result); @Override
latch.countDown(); public void run() {
list.add(result);
latch.countDown();
}
}); });
} }
// Wait for all the tasks to finish // Wait for all the tasks to finish
@@ -46,13 +49,16 @@ public class PoliteExecutorTest extends BrambleTestCase {
Executor delegate = Executors.newSingleThreadExecutor(); Executor delegate = Executors.newSingleThreadExecutor();
// Allow two tasks to be delegated at a time // Allow two tasks to be delegated at a time
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 2); PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 2);
List<Integer> list = new Vector<>(); final List<Integer> list = new Vector<Integer>();
CountDownLatch latch = new CountDownLatch(TASKS); final CountDownLatch latch = new CountDownLatch(TASKS);
for (int i = 0; i < TASKS; i++) { for (int i = 0; i < TASKS; i++) {
int result = i; final int result = i;
polite.execute(() -> { polite.execute(new Runnable() {
list.add(result); @Override
latch.countDown(); public void run() {
list.add(result);
latch.countDown();
}
}); });
} }
// Wait for all the tasks to finish // Wait for all the tasks to finish
@@ -67,20 +73,23 @@ public class PoliteExecutorTest extends BrambleTestCase {
Executor delegate = Executors.newCachedThreadPool(); Executor delegate = Executors.newCachedThreadPool();
// Allow all the tasks to be delegated straight away // Allow all the tasks to be delegated straight away
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2); PoliteExecutor polite = new PoliteExecutor(TAG, delegate, TASKS * 2);
List<Integer> list = new Vector<>(); final List<Integer> list = new Vector<Integer>();
CountDownLatch[] latches = new CountDownLatch[TASKS]; final CountDownLatch[] latches = new CountDownLatch[TASKS];
for (int i = 0; i < TASKS; i++) latches[i] = new CountDownLatch(1); for (int i = 0; i < TASKS; i++) latches[i] = new CountDownLatch(1);
for (int i = 0; i < TASKS; i++) { for (int i = 0; i < TASKS; i++) {
int result = i; final int result = i;
polite.execute(() -> { polite.execute(new Runnable() {
try { @Override
// Each task waits for the next task, if any, to finish public void run() {
if (result < TASKS - 1) latches[result + 1].await(); try {
list.add(result); // Each task waits for the next task, if any, to finish
} catch (InterruptedException e) { if (result < TASKS - 1) latches[result + 1].await();
fail(); list.add(result);
} catch (InterruptedException e) {
fail();
}
latches[result].countDown();
} }
latches[result].countDown();
}); });
} }
// Wait for all the tasks to finish // Wait for all the tasks to finish
@@ -95,19 +104,22 @@ public class PoliteExecutorTest extends BrambleTestCase {
Executor delegate = Executors.newCachedThreadPool(); Executor delegate = Executors.newCachedThreadPool();
// Allow one task to be delegated at a time // Allow one task to be delegated at a time
PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 1); PoliteExecutor polite = new PoliteExecutor(TAG, delegate, 1);
List<Integer> list = new Vector<>(); final List<Integer> list = new Vector<Integer>();
CountDownLatch latch = new CountDownLatch(TASKS); final CountDownLatch latch = new CountDownLatch(TASKS);
for (int i = 0; i < TASKS; i++) { for (int i = 0; i < TASKS; i++) {
int result = i; final int result = i;
polite.execute(() -> { polite.execute(new Runnable() {
try { @Override
// Each task runs faster than the previous task public void run() {
Thread.sleep(TASKS - result); try {
list.add(result); // Each task runs faster than the previous task
} catch (InterruptedException e) { Thread.sleep(TASKS - result);
fail(); list.add(result);
} catch (InterruptedException e) {
fail();
}
latch.countDown();
} }
latch.countDown();
}); });
} }
// Wait for all the tasks to finish // Wait for all the tasks to finish

View File

@@ -83,9 +83,9 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
@Test(expected = InvalidMessageException.class) @Test(expected = InvalidMessageException.class)
public void testRejectsTooShortMessage() throws Exception { public void testRejectsTooShortMessage() throws Exception {
byte[] invalidRaw = new byte[MESSAGE_HEADER_LENGTH]; final byte[] invalidRaw = new byte[MESSAGE_HEADER_LENGTH];
// Use a mock message so the length of the raw message can be invalid // Use a mock message so the length of the raw message can be invalid
Message invalidMessage = context.mock(Message.class); final Message invalidMessage = context.mock(Message.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(invalidMessage).getTimestamp(); oneOf(invalidMessage).getTimestamp();
@@ -101,8 +101,8 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
@Test @Test
public void testAcceptsMinLengthMessage() throws Exception { public void testAcceptsMinLengthMessage() throws Exception {
byte[] shortRaw = new byte[MESSAGE_HEADER_LENGTH + 1]; final byte[] shortRaw = new byte[MESSAGE_HEADER_LENGTH + 1];
Message shortMessage = final Message shortMessage =
new Message(messageId, groupId, timestamp, shortRaw); new Message(messageId, groupId, timestamp, shortRaw);
context.checking(new Expectations() {{ context.checking(new Expectations() {{

View File

@@ -76,8 +76,8 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testAddLocalMessage() throws Exception { public void testAddLocalMessage() throws Exception {
boolean shared = true; final boolean shared = true;
Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
@@ -95,7 +95,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testCreateMessage() throws Exception { public void testCreateMessage() throws Exception {
byte[] bytes = expectToByteArray(list); final byte[] bytes = expectToByteArray(list);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(messageFactory).createMessage(groupId, timestamp, bytes); oneOf(messageFactory).createMessage(groupId, timestamp, bytes);
@@ -107,7 +107,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testGetMessageAsList() throws Exception { public void testGetMessageAsList() throws Exception {
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
expectToList(true); expectToList(true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
@@ -125,7 +125,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testGetGroupMetadataAsDictionary() throws Exception { public void testGetGroupMetadataAsDictionary() throws Exception {
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
@@ -145,7 +145,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testGetMessageMetadataAsDictionary() throws Exception { public void testGetMessageMetadataAsDictionary() throws Exception {
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
@@ -165,9 +165,10 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testGetMessageMetadataAsDictionaryMap() throws Exception { public void testGetMessageMetadataAsDictionaryMap() throws Exception {
Map<MessageId, BdfDictionary> map = new HashMap<>(); final Map<MessageId, BdfDictionary> map =
new HashMap<MessageId, BdfDictionary>();
map.put(messageId, dictionary); map.put(messageId, dictionary);
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
@@ -187,13 +188,14 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testGetMessageMetadataAsDictionaryQuery() throws Exception { public void testGetMessageMetadataAsDictionaryQuery() throws Exception {
Map<MessageId, BdfDictionary> map = new HashMap<>(); final Map<MessageId, BdfDictionary> map =
new HashMap<MessageId, BdfDictionary>();
map.put(messageId, dictionary); map.put(messageId, dictionary);
BdfDictionary query = final BdfDictionary query =
BdfDictionary.of(new BdfEntry("query", "me")); BdfDictionary.of(new BdfEntry("query", "me"));
Metadata queryMetadata = new Metadata(); final Metadata queryMetadata = new Metadata();
queryMetadata.put("query", getRandomBytes(42)); queryMetadata.put("query", getRandomBytes(42));
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
@@ -215,7 +217,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testMergeGroupMetadata() throws Exception { public void testMergeGroupMetadata() throws Exception {
Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
@@ -233,7 +235,7 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testMergeMessageMetadata() throws Exception { public void testMergeMessageMetadata() throws Exception {
Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
@@ -280,10 +282,10 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testSign() throws Exception { public void testSign() throws Exception {
byte[] privateKey = getRandomBytes(42); final byte[] privateKey = getRandomBytes(42);
byte[] signed = getRandomBytes(42); final byte[] signed = getRandomBytes(42);
byte[] bytes = expectToByteArray(list); final byte[] bytes = expectToByteArray(list);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).sign(label, bytes, privateKey); oneOf(cryptoComponent).sign(label, bytes, privateKey);
will(returnValue(signed)); will(returnValue(signed));
@@ -295,8 +297,8 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testVerifySignature() throws Exception { public void testVerifySignature() throws Exception {
byte[] publicKey = getRandomBytes(42); final byte[] publicKey = getRandomBytes(42);
byte[] bytes = expectToByteArray(list); final byte[] bytes = expectToByteArray(list);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage); oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage);
@@ -309,8 +311,8 @@ public class ClientHelperImplTest extends BrambleTestCase {
@Test @Test
public void testVerifyWrongSignature() throws Exception { public void testVerifyWrongSignature() throws Exception {
byte[] publicKey = getRandomBytes(42); final byte[] publicKey = getRandomBytes(42);
byte[] bytes = expectToByteArray(list); final byte[] bytes = expectToByteArray(list);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage); oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage);
@@ -327,8 +329,8 @@ public class ClientHelperImplTest extends BrambleTestCase {
} }
} }
private byte[] expectToByteArray(BdfList list) throws Exception { private byte[] expectToByteArray(final BdfList list) throws Exception {
BdfWriter bdfWriter = context.mock(BdfWriter.class); final BdfWriter bdfWriter = context.mock(BdfWriter.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(bdfWriterFactory) oneOf(bdfWriterFactory)
@@ -339,8 +341,8 @@ public class ClientHelperImplTest extends BrambleTestCase {
return new byte[0]; return new byte[0];
} }
private void expectToList(boolean eof) throws Exception { private void expectToList(final boolean eof) throws Exception {
BdfReader bdfReader = context.mock(BdfReader.class); final BdfReader bdfReader = context.mock(BdfReader.class);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(bdfReaderFactory) oneOf(bdfReaderFactory)

View File

@@ -46,10 +46,10 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testAddContact() throws Exception { public void testAddContact() throws Exception {
SecretKey master = getSecretKey(); final SecretKey master = getSecretKey();
long timestamp = 42; final long timestamp = 42;
boolean alice = true; final boolean alice = true;
Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
@@ -64,13 +64,14 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).endTransaction(txn); oneOf(db).endTransaction(txn);
}}); }});
assertEquals(contactId, contactManager.addContact(remote, local, assertEquals(contactId, contactManager
master, timestamp, alice, verified, active)); .addContact(remote, local, master, timestamp, alice, verified,
active));
} }
@Test @Test
public void testGetContact() throws Exception { public void testGetContact() throws Exception {
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
@@ -85,8 +86,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testGetContactByAuthor() throws Exception { public void testGetContactByAuthor() throws Exception {
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = Collections.singleton(contact); final Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
@@ -101,7 +102,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test(expected = NoSuchContactException.class) @Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownAuthor() throws Exception { public void testGetContactByUnknownAuthor() throws Exception {
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
@@ -115,8 +116,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test(expected = NoSuchContactException.class) @Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownLocalAuthor() throws Exception { public void testGetContactByUnknownLocalAuthor() throws Exception {
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = Collections.singleton(contact); final Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
@@ -131,9 +132,10 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testActiveContacts() throws Exception { public void testActiveContacts() throws Exception {
Collection<Contact> activeContacts = Collections.singletonList(contact); Collection<Contact> activeContacts = Collections.singletonList(contact);
Collection<Contact> contacts = new ArrayList<>(activeContacts); final Collection<Contact> contacts =
new ArrayList<Contact>(activeContacts);
contacts.add(new Contact(new ContactId(3), remote, local, true, false)); contacts.add(new Contact(new ContactId(3), remote, local, true, false));
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));
@@ -148,7 +150,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testRemoveContact() throws Exception { public void testRemoveContact() throws Exception {
Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(false); oneOf(db).startTransaction(false);
will(returnValue(txn)); will(returnValue(txn));
@@ -164,7 +166,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testSetContactActive() throws Exception { public void testSetContactActive() throws Exception {
Transaction txn = new Transaction(null, false); final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).setContactActive(txn, contactId, active); oneOf(db).setContactActive(txn, contactId, active);
}}); }});
@@ -174,7 +176,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test @Test
public void testContactExists() throws Exception { public void testContactExists() throws Exception {
Transaction txn = new Transaction(null, true); final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(db).startTransaction(true); oneOf(db).startTransaction(true);
will(returnValue(txn)); will(returnValue(txn));

View File

@@ -1,25 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.KeyPair;
import java.security.GeneralSecurityException;
public class EcdsaSignatureTest extends SignatureTest {
@Override
protected KeyPair generateKeyPair() {
return crypto.generateSignatureKeyPair();
}
@Override
protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException {
return crypto.sign(label, toSign, privateKey);
}
@Override
protected boolean verify(String label, byte[] signedData, byte[] publicKey,
byte[] signature) throws GeneralSecurityException {
return crypto.verify(label, signedData, publicKey, signature);
}
}

View File

@@ -1,25 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.KeyPair;
import java.security.GeneralSecurityException;
public class EdSignatureTest extends SignatureTest {
@Override
protected KeyPair generateKeyPair() {
return crypto.generateEdKeyPair();
}
@Override
protected byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException {
return crypto.signEd(label, toSign, privateKey);
}
@Override
protected boolean verify(String label, byte[] signedData, byte[] publicKey,
byte[] signature) throws GeneralSecurityException {
return crypto.verifyEd(label, signedData, publicKey, signature);
}
}

View File

@@ -1,19 +1,16 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.spongycastle.asn1.sec.SECNamedCurves; import org.spongycastle.asn1.sec.SECNamedCurves;
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves; import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
import org.spongycastle.asn1.x9.X9ECParameters; import org.spongycastle.asn1.x9.X9ECParameters;
import org.spongycastle.crypto.AsymmetricCipherKeyPair; import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.BasicAgreement;
import org.spongycastle.crypto.Digest; import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement; import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
import org.spongycastle.crypto.generators.ECKeyPairGenerator; import org.spongycastle.crypto.generators.ECKeyPairGenerator;
import org.spongycastle.crypto.params.ECDomainParameters; import org.spongycastle.crypto.params.ECDomainParameters;
import org.spongycastle.crypto.params.ECKeyGenerationParameters; import org.spongycastle.crypto.params.ECKeyGenerationParameters;
import org.spongycastle.crypto.params.ECPrivateKeyParameters;
import org.spongycastle.crypto.params.ECPublicKeyParameters;
import org.spongycastle.crypto.params.ParametersWithRandom; import org.spongycastle.crypto.params.ParametersWithRandom;
import org.spongycastle.crypto.signers.DSADigestSigner; import org.spongycastle.crypto.signers.DSADigestSigner;
import org.spongycastle.crypto.signers.DSAKCalculator; import org.spongycastle.crypto.signers.DSAKCalculator;
@@ -22,23 +19,14 @@ import org.spongycastle.crypto.signers.HMacDSAKCalculator;
import org.spongycastle.math.ec.ECCurve; import org.spongycastle.math.ec.ECCurve;
import org.spongycastle.math.ec.ECPoint; import org.spongycastle.math.ec.ECPoint;
import org.spongycastle.math.ec.MontgomeryLadderMultiplier; import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
import org.whispersystems.curve25519.Curve25519;
import org.whispersystems.curve25519.Curve25519KeyPair;
import java.math.BigInteger; import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.Provider;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.Signature;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import static net.i2p.crypto.eddsa.EdDSAEngine.SIGNATURE_ALGORITHM;
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
// Not a JUnit test // Not a JUnit test
public class EllipticCurvePerformanceTest { public class EllipticCurvePerformanceTest {
@@ -49,9 +37,8 @@ public class EllipticCurvePerformanceTest {
"secp256k1", "secp256r1", "secp384r1", "secp521r1"); "secp256k1", "secp256r1", "secp384r1", "secp521r1");
private static final List<String> BRAINPOOL_NAMES = Arrays.asList( private static final List<String> BRAINPOOL_NAMES = Arrays.asList(
"brainpoolp256r1", "brainpoolp384r1", "brainpoolp512r1"); "brainpoolp256r1", "brainpoolp384r1", "brainpoolp512r1");
private static final Provider ED_PROVIDER = new EdDSASecurityProvider();
public static void main(String[] args) throws GeneralSecurityException { public static void main(String[] args) {
for (String name : SEC_NAMES) { for (String name : SEC_NAMES) {
ECDomainParameters params = ECDomainParameters params =
convertParams(SECNamedCurves.getByName(name)); convertParams(SECNamedCurves.getByName(name));
@@ -64,32 +51,43 @@ public class EllipticCurvePerformanceTest {
runTest(name + " default", params); runTest(name + " default", params);
runTest(name + " constant", constantTime(params)); runTest(name + " constant", constantTime(params));
} }
runTest("ours", PARAMETERS); runTest("ours", EllipticCurveConstants.PARAMETERS);
runCurve25519Test();
runEd25519Test();
} }
private static void runTest(String name, ECDomainParameters params) { private static void runTest(String name, ECDomainParameters params) {
// Generate two key pairs using the given parameters // Generate two key pairs using the given parameters
ECKeyGenerationParameters generatorParams =
new ECKeyGenerationParameters(params, random);
ECKeyPairGenerator generator = new ECKeyPairGenerator(); ECKeyPairGenerator generator = new ECKeyPairGenerator();
generator.init(new ECKeyGenerationParameters(params, random)); generator.init(generatorParams);
AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair(); AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair();
ECPublicKeyParameters public1 =
(ECPublicKeyParameters) keyPair1.getPublic();
ECPrivateKeyParameters private1 =
(ECPrivateKeyParameters) keyPair1.getPrivate();
AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair(); AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair();
// Time some ECDH and ECDHC key agreements ECPublicKeyParameters public2 =
long agreementMedian = runAgreementTest(keyPair1, keyPair2, false); (ECPublicKeyParameters) keyPair2.getPublic();
long agreementWithCofactorMedian = // Time some ECDH key agreements
runAgreementTest(keyPair1, keyPair2, true); List<Long> samples = new ArrayList<Long>();
for (int i = 0; i < SAMPLES; i++) {
ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
long start = System.nanoTime();
agreement.init(private1);
agreement.calculateAgreement(public2);
samples.add(System.nanoTime() - start);
}
long agreementMedian = median(samples);
// Time some signatures // Time some signatures
List<Long> samples = new ArrayList<>(); List<byte[]> signatures = new ArrayList<byte[]>();
List<byte[]> signatures = new ArrayList<>(); samples.clear();
for (int i = 0; i < SAMPLES; i++) { for (int i = 0; i < SAMPLES; i++) {
Digest digest = new Blake2sDigest(); Digest digest = new Blake2sDigest();
DSAKCalculator calculator = new HMacDSAKCalculator(digest); DSAKCalculator calculator = new HMacDSAKCalculator(digest);
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner( DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
calculator), digest); calculator), digest);
long start = System.nanoTime(); long start = System.nanoTime();
signer.init(true, signer.init(true, new ParametersWithRandom(private1, random));
new ParametersWithRandom(keyPair1.getPrivate(), random));
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN); signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
signatures.add(signer.generateSignature()); signatures.add(signer.generateSignature());
samples.add(System.nanoTime() - start); samples.add(System.nanoTime() - start);
@@ -103,83 +101,17 @@ public class EllipticCurvePerformanceTest {
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner( DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
calculator), digest); calculator), digest);
long start = System.nanoTime(); long start = System.nanoTime();
signer.init(false, keyPair1.getPublic()); signer.init(false, public1);
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN); signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
if (!signer.verifySignature(signatures.get(i))) if (!signer.verifySignature(signatures.get(i)))
throw new AssertionError(); throw new AssertionError();
samples.add(System.nanoTime() - start); samples.add(System.nanoTime() - start);
} }
long verificationMedian = median(samples); long verificationMedian = median(samples);
System.out.println(String.format("%s: %,d %,d %,d %,d", name, System.out.println(name + ": "
agreementMedian, agreementWithCofactorMedian, + agreementMedian + " "
signatureMedian, verificationMedian)); + signatureMedian + " "
} + verificationMedian);
private static long runAgreementTest(AsymmetricCipherKeyPair keyPair1,
AsymmetricCipherKeyPair keyPair2, boolean withCofactor) {
List<Long> samples = new ArrayList<>();
for (int i = 0; i < SAMPLES; i++) {
BasicAgreement agreement = createAgreement(withCofactor);
long start = System.nanoTime();
agreement.init(keyPair1.getPrivate());
agreement.calculateAgreement(keyPair2.getPublic());
samples.add(System.nanoTime() - start);
}
return median(samples);
}
private static BasicAgreement createAgreement(boolean withCofactor) {
if (withCofactor) return new ECDHCBasicAgreement();
else return new ECDHBasicAgreement();
}
private static void runCurve25519Test() {
Curve25519 curve25519 = Curve25519.getInstance("java");
Curve25519KeyPair keyPair1 = curve25519.generateKeyPair();
Curve25519KeyPair keyPair2 = curve25519.generateKeyPair();
// Time some key agreements
List<Long> samples = new ArrayList<>();
for (int i = 0; i < SAMPLES; i++) {
long start = System.nanoTime();
curve25519.calculateAgreement(keyPair1.getPublicKey(),
keyPair2.getPrivateKey());
samples.add(System.nanoTime() - start);
}
long agreementMedian = median(samples);
System.out.println(String.format("Curve25519: %,d - - -",
agreementMedian));
}
private static void runEd25519Test() throws GeneralSecurityException {
KeyPair keyPair = new KeyPairGenerator().generateKeyPair();
// Time some signatures
List<Long> samples = new ArrayList<>();
List<byte[]> signatures = new ArrayList<>();
for (int i = 0; i < SAMPLES; i++) {
Signature signature =
Signature.getInstance(SIGNATURE_ALGORITHM, ED_PROVIDER);
long start = System.nanoTime();
signature.initSign(keyPair.getPrivate(), random);
signature.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
signatures.add(signature.sign());
samples.add(System.nanoTime() - start);
}
long signatureMedian = median(samples);
// Time some signature verifications
samples.clear();
for (int i = 0; i < SAMPLES; i++) {
Signature signature =
Signature.getInstance(SIGNATURE_ALGORITHM, ED_PROVIDER);
long start = System.nanoTime();
signature.initVerify(keyPair.getPublic());
signature.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
if (!signature.verify(signatures.get(i)))
throw new AssertionError();
samples.add(System.nanoTime() - start);
}
long verificationMedian = median(samples);
System.out.println(String.format("Ed25519: - - %,d %,d",
signatureMedian, verificationMedian));
} }
private static long median(List<Long> list) { private static long median(List<Long> list) {

Some files were not shown because too many files have changed in this diff Show More