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
before_script:
- set -e
- export GRADLE_USER_HOME=$PWD/.gradle
# Accept the license for the Android build tools
- 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
- echo y | /opt/android-sdk/tools/bin/sdkmanager "build-tools;23.0.3"
test:
script:

View File

@@ -6,91 +6,99 @@ apply plugin: 'witness'
apply plugin: 'de.undercouch.download'
android {
compileSdkVersion 27
buildToolsVersion '26.0.2'
compileSdkVersion 23
buildToolsVersion '25.0.0'
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 1700
versionName "0.17.0"
targetSdkVersion 22
versionCode 1611
versionName "0.16.11"
consumerProguardFiles 'proguard-rules.txt'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
implementation project(path: ':bramble-core', configuration: 'default')
implementation fileTree(dir: 'libs', include: '*.jar')
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
compileOnly 'javax.annotation:jsr250-api:1.0'
compile project(path: ':bramble-core', configuration: 'default')
compile fileTree(dir: 'libs', include: '*.jar')
provided 'javax.annotation:jsr250-api:1.0'
}
dependencyVerification {
verify = [
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'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.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',
]
def torBinaryDir = 'src/main/res/raw'
task downloadTorGeoIp(type: Download) {
src 'https://briarproject.org/build/geoip-2017-09-06.zip'
dest "$torBinaryDir/geoip.zip"
onlyIfNewer true
}
ext.torBinaryDir = 'src/main/res/raw'
ext.torVersion = '0.2.9.12'
ext.geoipVersion = '2017-09-06'
ext.torDownloadUrl = 'https://briarproject.org/build/'
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
}
task downloadTorBinaryArm(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.12-arm.zip'
dest "$torBinaryDir/tor_arm.zip"
onlyIfNewer true
}
def verifyBinary(name, chksum) {
return tasks.create([
name : "verifyBinary${name}",
type : Verify,
dependsOn: downloadBinary(name)]) {
src "${torBinaryDir}/${name}.zip"
algorithm 'SHA-256'
checksum chksum
}
task downloadTorBinaryArmPie(type: Download) {
src 'https://briarproject.org/build/tor-0.2.9.12-arm-pie.zip'
dest "$torBinaryDir/tor_arm_pie.zip"
onlyIfNewer true
}
task downloadTorBinaryX86(type: Download) {
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 {
torBinaries.every { key, value ->
preBuild.dependsOn.add(verifyBinary(key, value))
preBuild.dependsOn {
[
'verifyTorGeoIp',
'verifyTorBinaryArm',
'verifyTorBinaryArmPie',
'verifyTorBinaryX86',
'verifyTorBinaryX86Pie'
]
}
}

View File

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

View File

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

View File

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

View File

@@ -370,45 +370,57 @@ class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
private void sendDevReports() {
ioExecutor.execute(() -> {
// TODO: Trigger this with a TransportEnabledEvent
File reportDir = AndroidUtils.getReportDir(appContext);
reporter.sendReports(reportDir);
ioExecutor.execute(new Runnable() {
@Override
public void run() {
// TODO: Trigger this with a TransportEnabledEvent
File reportDir = AndroidUtils.getReportDir(appContext);
reporter.sendReports(reportDir);
}
});
}
private void bind() {
ioExecutor.execute(() -> {
// If there's already a port number stored in config, reuse it
String portString = callback.getSettings().get(PREF_TOR_PORT);
int port;
if (StringUtils.isNullOrEmpty(portString)) port = 0;
else port = Integer.parseInt(portString);
// Bind a server socket to receive connections from Tor
ServerSocket ss = null;
try {
ss = new ServerSocket();
ss.bind(new InetSocketAddress("127.0.0.1", port));
} catch (IOException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
tryToClose(ss);
return;
ioExecutor.execute(new Runnable() {
@Override
public void run() {
// If there's already a port number stored in config, reuse it
String portString = callback.getSettings().get(PREF_TOR_PORT);
int port;
if (StringUtils.isNullOrEmpty(portString)) port = 0;
else port = Integer.parseInt(portString);
// Bind a server socket to receive connections from Tor
ServerSocket ss = null;
try {
ss = new ServerSocket();
ss.bind(new InetSocketAddress("127.0.0.1", port));
} catch (IOException e) {
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) {
ioExecutor.execute(() -> {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p);
if (d != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, d);
private void connectAndCallBack(final ContactId c,
final TransportProperties p) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
if (!isRunning()) return;
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() {
ioExecutor.execute(() -> {
if (!running) return;
ioExecutor.execute(new Runnable() {
@Override
public void run() {
if (!running) return;
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
ConnectivityManager cm = (ConnectivityManager) o;
NetworkInfo net = cm.getActiveNetworkInfo();
boolean online = net != null && net.isConnected();
boolean wifi = online && net.getType() == TYPE_WIFI;
String country = locationUtils.getCurrentCountry();
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
country);
Settings s = callback.getSettings();
int network = s.getInt(PREF_TOR_NETWORK, PREF_TOR_NETWORK_ALWAYS);
Object o = appContext.getSystemService(CONNECTIVITY_SERVICE);
ConnectivityManager cm = (ConnectivityManager) o;
NetworkInfo net = cm.getActiveNetworkInfo();
boolean online = net != null && net.isConnected();
boolean wifi = online && net.getType() == TYPE_WIFI;
String country = locationUtils.getCurrentCountry();
boolean blocked = TorNetworkMetadata.isTorProbablyBlocked(
country);
Settings s = callback.getSettings();
int network = s.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_ALWAYS);
if (LOG.isLoggable(INFO)) {
LOG.info("Online: " + online + ", wifi: " + wifi);
if ("".equals(country)) LOG.info("Country code unknown");
else LOG.info("Country code: " + country);
}
try {
if (!online) {
LOG.info("Disabling network, device is offline");
enableNetwork(false);
} else if (blocked) {
LOG.info("Disabling network, country is blocked");
enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_NEVER
|| (network == PREF_TOR_NETWORK_WIFI && !wifi)) {
LOG.info("Disabling network due to data setting");
enableNetwork(false);
} else {
LOG.info("Enabling network");
enableNetwork(true);
if (LOG.isLoggable(INFO)) {
LOG.info("Online: " + online + ", wifi: " + wifi);
if ("".equals(country)) LOG.info("Country code unknown");
else LOG.info("Country code: " + country);
}
try {
if (!online) {
LOG.info("Disabling network, device is offline");
enableNetwork(false);
} else if (blocked) {
LOG.info("Disabling network, country is blocked");
enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_NEVER
|| (network == PREF_TOR_NETWORK_WIFI && !wifi)) {
LOG.info("Disabling network due to data setting");
enableNetwork(false);
} else {
LOG.info("Enabling network");
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
AndroidExecutorImpl(Application app) {
uiHandler = new Handler(app.getApplicationContext().getMainLooper());
loop = () -> {
Looper.prepare();
backgroundHandler = new Handler();
startLatch.countDown();
Looper.loop();
loop = new Runnable() {
@Override
public void run() {
Looper.prepare();
backgroundHandler = new Handler();
startLatch.countDown();
Looper.loop();
}
};
}

View File

@@ -1,39 +1,30 @@
apply plugin: 'java-library'
sourceCompatibility = 1.8
targetCompatibility = 1.8
apply plugin: 'java'
sourceCompatibility = 1.6
targetCompatibility = 1.6
apply plugin: 'witness'
dependencies {
implementation "com.google.dagger:dagger:2.0.2"
implementation 'com.google.code.findbugs:jsr305:3.0.2'
compile "com.google.dagger:dagger:2.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'
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"
testCompile 'junit:junit:4.12'
testCompile "org.jmock:jmock:2.8.2"
testCompile "org.jmock:jmock-junit4:2.8.2"
testCompile "org.jmock:jmock-legacy:2.8.2"
testCompile "org.hamcrest:hamcrest-library:1.3"
testCompile "org.hamcrest:hamcrest-core:1.3"
}
dependencyVerification {
verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger:2.0.2:dagger-2.0.2.jar:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'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.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',
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
]
}
@@ -48,8 +39,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts {
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) {
this(dictionary, Collections.emptyList());
this(dictionary, Collections.<MessageId>emptyList());
}
public BdfDictionary getDictionary() {

View File

@@ -12,19 +12,18 @@ public interface ContactGroupFactory {
/**
* 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.
*/
Group createContactGroup(ClientId clientId, int clientVersion,
Contact contact);
Group createContactGroup(ClientId clientId, Contact contact);
/**
* Creates a group for the given client to share between the given authors
* identified by their AuthorIds.
*/
Group createContactGroup(ClientId clientId, int clientVersion,
AuthorId authorId1, AuthorId authorId2);
Group createContactGroup(ClientId clientId, AuthorId authorId1,
AuthorId authorId2);
}

View File

@@ -12,32 +12,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
@NotNullByDefault
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.
*/

View File

@@ -1,5 +1,8 @@
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.SecureRandom;
@@ -17,98 +20,156 @@ public interface CryptoComponent {
KeyParser getSignatureKeyParser();
KeyPair generateEdKeyPair();
KeyParser getEdKeyParser();
KeyParser getMessageKeyParser();
/**
* Derives another secret key from the given secret key.
*
* @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
* Derives a stream header key from the given master secret.
* @param alice whether the key is for use by Alice or Bob.
*/
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
* corresponding private keys.
* <p/>
* Part of BQP.
*
* @param label a namespaced label indicating the purpose of this shared
* secret, to prevent it from being repurposed or colliding with a shared
* secret derived for another purpose
* @param theirPublicKey the public key of the remote party
* @param ourKeyPair the key pair of the local party
* @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 deriveSharedSecret(String label, PublicKey theirPublicKey,
KeyPair ourKeyPair, byte[]... inputs)
throws GeneralSecurityException;
SecretKey deriveSharedSecret(byte[] theirPublicKey, KeyPair ourKeyPair,
boolean alice) 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
* signature, to prevent it from being repurposed or colliding with a
* signature created for another purpose
* @param sharedSecret the common shared secret
* @param theirPayload the commit payload from the remote party
* @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)
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
*/
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.
*/
boolean verify(String label, byte[] signedData, byte[] publicKey,
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
* combined by prefixing each input with its length.
*
* @param label a namespaced label indicating the purpose of this hash, to
* prevent it from being repurposed or colliding with a hash created for
* another purpose
* @param label A label specific to this hash to ensure that hashes
* calculated for distinct purposes don't collide.
*/
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
* given inputs. The inputs are unambiguously combined by prefixing each
* 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

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 java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentSkipListMap;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
public class BdfDictionary extends TreeMap<String, Object> {
public class BdfDictionary extends ConcurrentSkipListMap<String, 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.FormatException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Vector;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.bramble.api.data.BdfDictionary.NULL_VALUE;
@NotThreadSafe
public class BdfList extends ArrayList<Object> {
public class BdfList extends Vector<Object> {
/**
* Factory method for constructing lists inline.

View File

@@ -122,9 +122,8 @@ public interface DatabaseComponent {
throws DbException;
/**
* Deletes the message with the given ID. Unlike
* {@link #removeMessage(Transaction, MessageId)}, the message ID and any
* other associated data are not deleted.
* Deletes the message with the given ID. The message ID and any other
* associated data are not deleted.
*/
void deleteMessage(Transaction txn, MessageId m) throws DbException;
@@ -453,11 +452,6 @@ public interface DatabaseComponent {
*/
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.
*/

View File

@@ -1,11 +1,11 @@
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
public class Metadata extends TreeMap<String, byte[]> {
@ThreadSafe
public class Metadata extends Hashtable<String, byte[]> {
/**
* Special value to indicate that a key is being removed.

View File

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

View File

@@ -16,7 +16,7 @@ public class AuthorId extends UniqueId {
/**
* 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) {
super(id);

View File

@@ -5,7 +5,7 @@ public interface KeyAgreementConstants {
/**
* The current version of the BQP protocol.
*/
byte PROTOCOL_VERSION = 3;
byte PROTOCOL_VERSION = 2;
/**
* The length of the record header in bytes.
@@ -22,10 +22,7 @@ public interface KeyAgreementConstants {
*/
int COMMIT_LENGTH = 16;
/**
* The connection timeout in milliseconds.
*/
long CONNECTION_TIMEOUT = 20 * 1000;
long CONNECTION_TIMEOUT = 20 * 1000; // Milliseconds
/**
* The transport identifier for Bluetooth.
@@ -36,16 +33,4 @@ public interface KeyAgreementConstants {
* The transport identifier for LAN.
*/
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;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothEnableDisableReason;
import javax.annotation.concurrent.Immutable;
@@ -11,5 +11,8 @@ import javax.annotation.concurrent.Immutable;
*/
@Immutable
@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.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothEnableDisableReason;
import javax.annotation.concurrent.Immutable;
@@ -10,5 +11,8 @@ import javax.annotation.concurrent.Immutable;
*/
@Immutable
@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");
/**
* The current version of the transport property client.
*/
int CLIENT_VERSION = 0;
/**
* Stores the given properties received while adding a contact - they will
* be superseded by any properties synced from the contact.
@@ -38,7 +33,7 @@ public interface TransportPropertyManager {
/**
* Returns the local transport properties for all transports.
* <br/>
* TODO: Transaction can be read-only when code is simplified
* Read-Only
*/
Map<TransportId, TransportProperties> getLocalProperties(Transaction txn)
throws DbException;

View File

@@ -36,8 +36,4 @@ public class ClientId implements Comparable<ClientId> {
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 {
/**
* 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.
*/
public static final String LABEL = "org.briarproject.bramble/GROUP_ID";
public static final String LABEL = "org.briarproject.bramble.GROUP_ID";
public GroupId(byte[] id) {
super(id);

View File

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

View File

@@ -16,7 +16,7 @@ public class MessageId extends UniqueId {
/**
* 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) {
super(id);

View File

@@ -7,7 +7,7 @@ public interface TransportConstants {
/**
* The current version of the transport protocol.
*/
int PROTOCOL_VERSION = 4;
int PROTOCOL_VERSION = 3;
/**
* The length of the pseudo-random tag in bytes.
@@ -80,32 +80,4 @@ public interface TransportConstants {
* The size of the reordering window.
*/
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.junit.After;
public abstract class BrambleMockTestCase extends BrambleTestCase {
public abstract class BrambleMockTestCase extends
BrambleTestCase {
protected final Mockery context = new Mockery();

View File

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

View File

@@ -1,60 +1,28 @@
apply plugin: 'java-library'
sourceCompatibility = 1.8
targetCompatibility = 1.8
plugins {
id 'java'
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'
dependencies {
implementation project(path: ':bramble-api', configuration: 'default')
implementation 'com.madgag.spongycastle:core:1.58.0.0'
implementation 'com.h2database:h2:1.4.192' // The last version that supports Java 1.6
implementation 'org.bitlet:weupnp:0.1.4'
implementation 'net.i2p.crypto:eddsa:0.2.0'
compile project(path: ':bramble-api', configuration: 'default')
compile 'com.madgag.spongycastle:core:1.58.0.0'
compile 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
compile 'org.bitlet:weupnp:0.1.4'
apt 'com.google.dagger:dagger-compiler:2.0.2'
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'
testCompile project(path: ':bramble-api', configuration: 'testOutput')
}
dependencyVerification {
verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger-compiler:2.0.2:dagger-compiler-2.0.2.jar:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'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',
'com.madgag.spongycastle:core:199617dd5698c5a9312b898c0a4cec7ce9dd8649d07f65d91629f58229d72728',
'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
]
}
@@ -69,8 +37,3 @@ task jarTest(type: Jar, dependsOn: testClasses) {
artifacts {
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();
@GuardedBy("lock")
private final Queue<Runnable> queue = new LinkedList<>();
private final Queue<Runnable> queue = new LinkedList<Runnable>();
private final Executor delegate;
private final int maxConcurrentTasks;
private final Logger log;
@@ -48,17 +48,20 @@ public class PoliteExecutor implements Executor {
}
@Override
public void execute(Runnable r) {
long submitted = System.currentTimeMillis();
Runnable wrapped = () -> {
if (log.isLoggable(LOG_LEVEL)) {
long queued = System.currentTimeMillis() - submitted;
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
}
try {
r.run();
} finally {
scheduleNext();
public void execute(final Runnable r) {
final long submitted = System.currentTimeMillis();
Runnable wrapped = new Runnable() {
@Override
public void run() {
if (log.isLoggable(LOG_LEVEL)) {
long queued = System.currentTimeMillis() - submitted;
log.log(LOG_LEVEL, "Queue time " + queued + " ms");
}
try {
r.run();
} finally {
scheduleNext();
}
}
};
synchronized (lock) {

View File

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

View File

@@ -201,7 +201,8 @@ class ClientHelperImpl implements ClientHelper {
public Map<MessageId, BdfDictionary> getMessageMetadataAsDictionary(
Transaction txn, GroupId g) throws DbException, FormatException {
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())
parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
return parsed;
@@ -228,7 +229,8 @@ class ClientHelperImpl implements ClientHelper {
FormatException {
Metadata metadata = metadataEncoder.encode(query);
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())
parsed.put(e.getKey(), metadataParser.parse(e.getValue()));
return parsed;

View File

@@ -32,25 +32,23 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
}
@Override
public Group createLocalGroup(ClientId clientId, int clientVersion) {
return groupFactory.createGroup(clientId, clientVersion,
LOCAL_GROUP_DESCRIPTOR);
public Group createLocalGroup(ClientId clientId) {
return groupFactory.createGroup(clientId, LOCAL_GROUP_DESCRIPTOR);
}
@Override
public Group createContactGroup(ClientId clientId, int clientVersion,
Contact contact) {
public Group createContactGroup(ClientId clientId, Contact contact) {
AuthorId local = contact.getLocalAuthorId();
AuthorId remote = contact.getAuthor().getId();
byte[] descriptor = createGroupDescriptor(local, remote);
return groupFactory.createGroup(clientId, clientVersion, descriptor);
return groupFactory.createGroup(clientId, descriptor);
}
@Override
public Group createContactGroup(ClientId clientId, int clientVersion,
AuthorId authorId1, AuthorId authorId2) {
public Group createContactGroup(ClientId clientId, AuthorId authorId1,
AuthorId authorId2) {
byte[] descriptor = createGroupDescriptor(authorId1, authorId2);
return groupFactory.createGroup(clientId, clientVersion, descriptor);
return groupFactory.createGroup(clientId, descriptor);
}
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
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
masterSecret, new byte[] {PROTOCOL_VERSION});
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
SecretKey aliceHeaderKey = crypto.deriveHeaderKey(masterSecret, true);
SecretKey bobHeaderKey = crypto.deriveHeaderKey(masterSecret, false);
// Create the readers
InputStream streamReader =
@@ -158,10 +156,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
BdfWriter w = bdfWriterFactory.createWriter(streamWriter);
// Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION});
byte[] aliceNonce = crypto.deriveSignatureNonce(masterSecret, true);
byte[] bobNonce = crypto.deriveSignatureNonce(masterSecret, false);
// Exchange pseudonyms, signed nonces, and timestamps
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
w.close();
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);
listener.contactExchangeFailed();
tryToClose(conn, true);
@@ -200,8 +201,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
try {
// Add the contact
ContactId contactId = addContact(remoteAuthor, timestamp,
remoteProperties);
ContactId contactId = addContact(remoteAuthor, masterSecret,
timestamp, alice, remoteProperties);
// Reuse the connection as a transport connection
connectionManager.manageOutgoingConnection(contactId, transportId,
conn);
@@ -275,7 +276,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private Map<TransportId, TransportProperties> receiveTransportProperties(
BdfReader r) throws IOException {
Map<TransportId, TransportProperties> remote = new HashMap<>();
Map<TransportId, TransportProperties> remote =
new HashMap<TransportId, TransportProperties>();
r.readListStart();
while (!r.hasListEnd()) {
r.readListStart();
@@ -298,15 +300,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
return remote;
}
private ContactId addContact(Author remoteAuthor, long timestamp,
private ContactId addContact(Author remoteAuthor, SecretKey master,
long timestamp, boolean alice,
Map<TransportId, TransportProperties> remoteProperties)
throws DbException {
ContactId contactId;
Transaction txn = db.startTransaction(false);
try {
contactId = contactManager.addContact(txn, remoteAuthor,
localAuthor.getId(), masterSecret, timestamp, alice,
true, true);
localAuthor.getId(), master, timestamp, alice, true, true);
transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties);
db.commitTransaction(txn);
@@ -316,7 +318,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
return contactId;
}
private void tryToClose(DuplexTransportConnection conn, boolean exception) {
private void tryToClose(DuplexTransportConnection conn,
boolean exception) {
try {
LOG.info("Closing connection");
conn.getReader().dispose(exception, true);

View File

@@ -34,8 +34,8 @@ class ContactManagerImpl implements ContactManager {
ContactManagerImpl(DatabaseComponent db, KeyManager keyManager) {
this.db = db;
this.keyManager = keyManager;
addHooks = new CopyOnWriteArrayList<>();
removeHooks = new CopyOnWriteArrayList<>();
addHooks = new CopyOnWriteArrayList<AddContactHook>();
removeHooks = new CopyOnWriteArrayList<RemoveContactHook>();
}
@Override
@@ -125,7 +125,7 @@ class ContactManagerImpl implements ContactManager {
} finally {
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);
return active;
}

View File

@@ -1,16 +1,16 @@
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.KeyPair;
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.crypto.SecretKey;
import org.briarproject.bramble.api.plugin.TransportId;
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.StringUtils;
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.KeyParameter;
import java.nio.charset.Charset;
import java.security.GeneralSecurityException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
@@ -39,8 +40,14 @@ import java.util.logging.Logger;
import javax.inject.Inject;
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.util.ByteUtils.INT_16_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 {
@@ -49,19 +56,49 @@ class CryptoComponentImpl implements CryptoComponent {
private static final int AGREEMENT_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 PBKDF_SALT_BYTES = 32; // 256 bits
private static final int PBKDF_TARGET_MILLIS = 500;
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 ECKeyPairGenerator agreementKeyPairGenerator;
private final ECKeyPairGenerator signatureKeyPairGenerator;
private final KeyParser agreementKeyParser, signatureKeyParser;
private final MessageEncrypter messageEncrypter;
private final KeyPairGenerator edKeyPairGenerator;
private final KeyParser edKeyParser;
@Inject
CryptoComponentImpl(SecureRandomProvider secureRandomProvider) {
@@ -95,9 +132,6 @@ class CryptoComponentImpl implements CryptoComponent {
signatureKeyParser = new Sec1KeyParser(PARAMETERS,
SIGNATURE_KEY_PAIR_BITS);
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
@@ -156,21 +190,6 @@ class CryptoComponentImpl implements CryptoComponent {
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
public KeyPair generateAgreementKeyPair() {
AsymmetricCipherKeyPair keyPair =
@@ -219,63 +238,197 @@ class CryptoComponentImpl implements CryptoComponent {
}
@Override
public SecretKey deriveKey(String label, SecretKey k, byte[]... inputs) {
byte[] mac = mac(label, k, inputs);
if (mac.length != SecretKey.LENGTH) throw new IllegalStateException();
return new SecretKey(mac);
public SecretKey deriveHeaderKey(SecretKey master,
boolean alice) {
return new SecretKey(macKdf(master, alice ? A_INVITE : B_INVITE));
}
@Override
public SecretKey deriveSharedSecret(String label, PublicKey theirPublicKey,
KeyPair ourKeyPair, byte[]... inputs)
throws GeneralSecurityException {
public SecretKey deriveMacKey(SecretKey master, boolean alice) {
return new SecretKey(macKdf(master, alice ? A_MAC : B_MAC));
}
@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();
byte[][] hashInputs = new byte[inputs.length + 1][];
hashInputs[0] = performRawKeyAgreement(ourPriv, theirPublicKey);
System.arraycopy(inputs, 0, hashInputs, 1, inputs.length);
byte[] hash = hash(label, hashInputs);
if (hash.length != SecretKey.LENGTH) throw new IllegalStateException();
return new SecretKey(hash);
PublicKey theirPub = agreementKeyParser.parsePublicKey(theirPublicKey);
byte[] raw = performRawKeyAgreement(ourPriv, theirPub);
byte[] alicePub, bobPub;
if (alice) {
alicePub = ourKeyPair.getPublic().getEncoded();
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
public byte[] sign(String label, byte[] toSign, byte[] privateKey)
throws GeneralSecurityException {
return sign(new SignatureImpl(secureRandom), signatureKeyParser, label,
toSign, privateKey);
}
@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 {
Signature signature = new SignatureImpl(secureRandom);
KeyParser keyParser = getSignatureKeyParser();
PrivateKey key = keyParser.parsePrivateKey(privateKey);
sig.initSign(key);
updateSignature(sig, label, toSign);
return sig.sign();
signature.initSign(key);
updateSignature(signature, label, toSign);
return signature.sign();
}
@Override
public boolean verify(String label, byte[] signedData, byte[] publicKey,
byte[] signature) throws GeneralSecurityException {
return verify(new SignatureImpl(secureRandom), signatureKeyParser,
label, signedData, publicKey, signature);
}
@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 {
Signature sig = new SignatureImpl(secureRandom);
KeyParser keyParser = getSignatureKeyParser();
PublicKey key = keyParser.parsePublicKey(publicKey);
sig.initVerify(key);
updateSignature(sig, label, signedData);
@@ -283,7 +436,7 @@ class CryptoComponentImpl implements CryptoComponent {
}
private void updateSignature(Signature signature, String label,
byte[] toSign) throws GeneralSecurityException {
byte[] toSign) {
byte[] labelBytes = StringUtils.toUtf8(label);
byte[] length = new byte[INT_32_BYTES];
ByteUtils.writeUint32(labelBytes.length, length, 0);
@@ -313,13 +466,14 @@ class CryptoComponentImpl implements CryptoComponent {
}
@Override
public byte[] mac(String label, SecretKey macKey, byte[]... inputs) {
byte[] labelBytes = StringUtils.toUtf8(label);
public int getHashLength() {
return HASH_SIZE;
}
@Override
public byte[] mac(SecretKey macKey, byte[]... inputs) {
Digest mac = new Blake2sDigest(macKey.getBytes());
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) {
ByteUtils.writeUint32(input.length, length, 0);
mac.update(length, 0, length.length);
@@ -411,6 +565,30 @@ class CryptoComponentImpl implements CryptoComponent {
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
private byte[] pbkdf2(String password, byte[] salt, int iterations) {
byte[] utf8 = StringUtils.toUtf8(password);
@@ -424,8 +602,8 @@ class CryptoComponentImpl implements CryptoComponent {
// Package access for testing
int chooseIterationCount(int targetMillis) {
List<Long> quickSamples = new ArrayList<>(PBKDF_SAMPLES);
List<Long> slowSamples = new ArrayList<>(PBKDF_SAMPLES);
List<Long> quickSamples = new ArrayList<Long>(PBKDF_SAMPLES);
List<Long> slowSamples = new ArrayList<Long>(PBKDF_SAMPLES);
long iterationNanos = 0, initNanos = 0;
while (iterationNanos <= 0 || initNanos <= 0) {
// 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.api.crypto.CryptoComponent;
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.StreamDecrypterFactory;
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.system.SecureRandomProvider;
@@ -50,7 +48,7 @@ public class CryptoModule {
public CryptoModule() {
// Use an unbounded queue
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>();
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>();
// Discard tasks that are submitted during shutdown
RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy();
@@ -76,12 +74,6 @@ public class CryptoModule {
return new PasswordStrengthEstimatorImpl();
}
@Provides
TransportCrypto provideTransportCrypto(
TransportCryptoImpl transportCrypto) {
return transportCrypto;
}
@Provides
StreamDecrypterFactory provideStreamDecrypterFactory(
Provider<AuthenticatedCipher> cipherProvider) {
@@ -89,17 +81,9 @@ public class CryptoModule {
}
@Provides
StreamEncrypterFactory provideStreamEncrypterFactory(
CryptoComponent crypto, TransportCrypto transportCrypto,
StreamEncrypterFactory provideStreamEncrypterFactory(CryptoComponent crypto,
Provider<AuthenticatedCipher> cipherProvider) {
return new StreamEncrypterFactoryImpl(crypto, transportCrypto,
cipherProvider);
}
@Provides
KeyAgreementCrypto provideKeyAgreementCrypto(
KeyAgreementCryptoImpl keyAgreementCrypto) {
return keyAgreementCrypto;
return new StreamEncrypterFactoryImpl(crypto, cipherProvider);
}
@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
public float estimateStrength(String password) {
HashSet<Character> unique = new HashSet<>();
HashSet<Character> unique = new HashSet<Character>();
int length = password.length();
for (int i = 0; i < length; i++) unique.add(password.charAt(i));
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)}
*/
void update(byte b) throws GeneralSecurityException;
void update(byte b);
/**
* @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)}
*/
void update(byte[] b, int off, int len) throws GeneralSecurityException;
void update(byte[] b, int off, int len);
/**
* @see {@link java.security.Signature#sign()}
*/
byte[] sign() throws GeneralSecurityException;
byte[] sign();
/**
* @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.StreamEncrypter;
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.transport.StreamContext;
@@ -23,15 +22,12 @@ import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENG
class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
private final CryptoComponent crypto;
private final TransportCrypto transportCrypto;
private final Provider<AuthenticatedCipher> cipherProvider;
@Inject
StreamEncrypterFactoryImpl(CryptoComponent crypto,
TransportCrypto transportCrypto,
Provider<AuthenticatedCipher> cipherProvider) {
this.crypto = crypto;
this.transportCrypto = transportCrypto;
this.cipherProvider = cipherProvider;
}
@@ -41,8 +37,7 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
AuthenticatedCipher cipher = cipherProvider.get();
long streamNumber = ctx.getStreamNumber();
byte[] tag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION,
streamNumber);
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
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 =
new ReentrantReadWriteLock(true);
private volatile int shutdownHandle = -1;
@Inject
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
ShutdownManager shutdown) {
@@ -101,20 +103,26 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
public boolean open() throws DbException {
boolean reopened = db.open();
shutdown.addShutdownHook(() -> {
try {
close();
} catch (DbException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
Runnable shutdownHook = new Runnable() {
@Override
public void run() {
try {
close();
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
};
boolean reopened = db.open();
shutdownHandle = shutdown.addShutdownHook(shutdownHook);
return reopened;
}
@Override
public void close() throws DbException {
if (closed.getAndSet(true)) return;
shutdown.removeShutdownHook(shutdownHandle);
db.close();
}
@@ -133,7 +141,11 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
try {
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();
else lock.writeLock().unlock();
throw e;
@@ -319,7 +331,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
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) {
messages.add(db.getRawMessage(txn, m));
db.updateExpiryTime(txn, c, m, maxLatency);
@@ -369,7 +381,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
throw new NoSuchContactException();
Collection<MessageId> ids = db.getRequestedMessagesToSend(txn, c,
maxLength);
List<byte[]> messages = new ArrayList<>(ids.size());
List<byte[]> messages = new ArrayList<byte[]>(ids.size());
for (MessageId m : ids) {
messages.add(db.getRawMessage(txn, m));
db.updateExpiryTime(txn, c, m, maxLatency);
@@ -649,7 +661,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
Collection<MessageId> acked = new ArrayList<>();
Collection<MessageId> acked = new ArrayList<MessageId>();
for (MessageId m : a.getMessageIds()) {
if (db.containsVisibleMessage(txn, c, m)) {
db.raiseSeenFlag(txn, c, m);
@@ -758,16 +770,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public void removeTransport(Transaction transaction, TransportId t)
throws DbException {
@@ -884,7 +886,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
Map<ContactId, TransportKeys> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
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()) {
ContactId c = e.getKey();
TransportKeys k = e.getValue();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,10 +1,19 @@
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.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.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.Provides;
@@ -13,9 +22,13 @@ import dagger.Provides;
public class KeyAgreementModule {
@Provides
KeyAgreementTask provideKeyAgreementTask(
KeyAgreementTaskImpl keyAgreementTask) {
return keyAgreementTask;
@Singleton
KeyAgreementTaskFactory provideKeyAgreementTaskFactory(Clock clock,
CryptoComponent crypto, EventBus eventBus,
@IoExecutor Executor ioExecutor, PayloadEncoder payloadEncoder,
PluginManager pluginManager) {
return new KeyAgreementTaskFactoryImpl(clock, crypto, eventBus,
ioExecutor, payloadEncoder, pluginManager);
}
@Provides

View File

@@ -1,10 +1,7 @@
package org.briarproject.bramble.keyagreement;
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.KeyParser;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
@@ -14,10 +11,6 @@ import java.io.IOException;
import java.security.GeneralSecurityException;
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.
* <p/>
@@ -64,7 +57,6 @@ class KeyAgreementProtocol {
private final Callbacks callbacks;
private final CryptoComponent crypto;
private final KeyAgreementCrypto keyAgreementCrypto;
private final PayloadEncoder payloadEncoder;
private final KeyAgreementTransport transport;
private final Payload theirPayload, ourPayload;
@@ -72,13 +64,11 @@ class KeyAgreementProtocol {
private final boolean alice;
KeyAgreementProtocol(Callbacks callbacks, CryptoComponent crypto,
KeyAgreementCrypto keyAgreementCrypto,
PayloadEncoder payloadEncoder, KeyAgreementTransport transport,
Payload theirPayload, Payload ourPayload, KeyPair ourKeyPair,
boolean alice) {
this.callbacks = callbacks;
this.crypto = crypto;
this.keyAgreementCrypto = keyAgreementCrypto;
this.payloadEncoder = payloadEncoder;
this.transport = transport;
this.theirPayload = theirPayload;
@@ -96,7 +86,7 @@ class KeyAgreementProtocol {
*/
SecretKey perform() throws AbortException, IOException {
try {
PublicKey theirPublicKey;
byte[] theirPublicKey;
if (alice) {
sendKey();
// Alice waits here until Bob obtains her payload.
@@ -114,7 +104,7 @@ class KeyAgreementProtocol {
receiveConfirm(s, theirPublicKey);
sendConfirm(s, theirPublicKey);
}
return crypto.deriveKey(MASTER_SECRET_LABEL, s);
return crypto.deriveMasterSecret(s);
} catch (AbortException e) {
sendAbort(e.getCause() != null);
throw e;
@@ -125,41 +115,27 @@ class KeyAgreementProtocol {
transport.sendKey(ourKeyPair.getPublic().getEncoded());
}
private PublicKey receiveKey() throws AbortException {
byte[] publicKeyBytes = transport.receiveKey();
private byte[] receiveKey() throws AbortException {
byte[] publicKey = transport.receiveKey();
callbacks.initialRecordReceived();
KeyParser keyParser = crypto.getAgreementKeyParser();
try {
PublicKey publicKey = keyParser.parsePublicKey(publicKeyBytes);
byte[] expected = keyAgreementCrypto.deriveKeyCommitment(publicKey);
if (!Arrays.equals(expected, theirPayload.getCommitment()))
throw new AbortException();
return publicKey;
} catch (GeneralSecurityException e) {
byte[] expected = crypto.deriveKeyCommitment(publicKey);
if (!Arrays.equals(expected, theirPayload.getCommitment()))
throw new AbortException();
}
return publicKey;
}
private SecretKey deriveSharedSecret(PublicKey theirPublicKey)
private SecretKey deriveSharedSecret(byte[] theirPublicKey)
throws AbortException {
try {
byte[] ourPublicKeyBytes = ourKeyPair.getPublic().getEncoded();
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);
return crypto.deriveSharedSecret(theirPublicKey, ourKeyPair, alice);
} catch (GeneralSecurityException e) {
throw new AbortException(e);
}
}
private void sendConfirm(SecretKey s, PublicKey theirPublicKey)
private void sendConfirm(SecretKey s, byte[] theirPublicKey)
throws IOException {
byte[] confirm = keyAgreementCrypto.deriveConfirmationRecord(s,
byte[] confirm = crypto.deriveConfirmationRecord(s,
payloadEncoder.encode(theirPayload),
payloadEncoder.encode(ourPayload),
theirPublicKey, ourKeyPair,
@@ -167,10 +143,10 @@ class KeyAgreementProtocol {
transport.sendConfirm(confirm);
}
private void receiveConfirm(SecretKey s, PublicKey theirPublicKey)
private void receiveConfirm(SecretKey s, byte[] theirPublicKey)
throws AbortException {
byte[] confirm = transport.receiveConfirm();
byte[] expected = keyAgreementCrypto.deriveConfirmationRecord(s,
byte[] expected = crypto.deriveConfirmationRecord(s,
payloadEncoder.encode(theirPayload),
payloadEncoder.encode(ourPayload),
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;
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.SecretKey;
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.KeyAgreementStartedEvent;
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.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginManager;
@@ -25,8 +23,6 @@ import java.io.IOException;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault
@@ -39,7 +35,6 @@ class KeyAgreementTaskImpl extends Thread implements
Logger.getLogger(KeyAgreementTaskImpl.class.getName());
private final CryptoComponent crypto;
private final KeyAgreementCrypto keyAgreementCrypto;
private final EventBus eventBus;
private final PayloadEncoder payloadEncoder;
private final KeyPair localKeyPair;
@@ -48,17 +43,14 @@ class KeyAgreementTaskImpl extends Thread implements
private Payload localPayload;
private Payload remotePayload;
@Inject
KeyAgreementTaskImpl(Clock clock, CryptoComponent crypto,
KeyAgreementCrypto keyAgreementCrypto, EventBus eventBus,
PayloadEncoder payloadEncoder, PluginManager pluginManager,
@IoExecutor Executor ioExecutor) {
EventBus eventBus, PayloadEncoder payloadEncoder,
PluginManager pluginManager, Executor ioExecutor) {
this.crypto = crypto;
this.keyAgreementCrypto = keyAgreementCrypto;
this.eventBus = eventBus;
this.payloadEncoder = payloadEncoder;
localKeyPair = crypto.generateAgreementKeyPair();
connector = new KeyAgreementConnector(this, clock, keyAgreementCrypto,
connector = new KeyAgreementConnector(this, clock, crypto,
pluginManager, ioExecutor);
}
@@ -108,8 +100,8 @@ class KeyAgreementTaskImpl extends Thread implements
// Run BQP protocol over the connection
LOG.info("Starting BQP protocol");
KeyAgreementProtocol protocol = new KeyAgreementProtocol(this, crypto,
keyAgreementCrypto, payloadEncoder, transport, remotePayload,
localPayload, localKeyPair, alice);
payloadEncoder, transport, remotePayload, localPayload,
localKeyPair, alice);
try {
SecretKey master = protocol.perform();
KeyAgreementResult result =

View File

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

View File

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

View File

@@ -37,7 +37,7 @@ public class LifecycleModule {
public LifecycleModule() {
// 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
RejectedExecutionHandler policy =
new ThreadPoolExecutor.DiscardPolicy();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -63,7 +63,7 @@ class LanTcpPlugin extends TcpPlugin {
TransportProperties p = callback.getLocalProperties();
String oldIpPorts = p.get(PROP_IP_PORTS);
List<InetSocketAddress> olds = parseSocketAddresses(oldIpPorts);
List<InetSocketAddress> locals = new LinkedList<>();
List<InetSocketAddress> locals = new LinkedList<InetSocketAddress>();
for (InetAddress local : getLocalIpAddresses()) {
if (isAcceptableAddress(local)) {
// 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) {
if (StringUtils.isNullOrEmpty(ipPorts)) return Collections.emptyList();
String[] split = ipPorts.split(SEPARATOR);
List<InetSocketAddress> addresses = new ArrayList<>();
List<InetSocketAddress> addresses = new ArrayList<InetSocketAddress>();
for (String ipPort : split) {
InetSocketAddress a = parseSocketAddress(ipPort);
if (a != null) addresses.add(a);
@@ -95,7 +95,7 @@ class LanTcpPlugin extends TcpPlugin {
String ipPort = getIpPortString(a);
// Get the list of recently used addresses
String setting = callback.getSettings().get(PREF_LAN_IP_PORTS);
List<String> recent = new ArrayList<>();
List<String> recent = new ArrayList<String>();
if (!StringUtils.isNullOrEmpty(setting))
Collections.addAll(recent, setting.split(SEPARATOR));
// Is the address already in the list?
@@ -111,7 +111,7 @@ class LanTcpPlugin extends TcpPlugin {
recent = recent.subList(0, MAX_ADDRESSES);
setting = StringUtils.join(recent, SEPARATOR);
// Update the list of addresses shared with contacts
List<String> shared = new ArrayList<>(recent);
List<String> shared = new ArrayList<String>(recent);
Collections.sort(shared);
String property = StringUtils.join(shared, SEPARATOR);
TransportProperties properties = new TransportProperties();
@@ -260,12 +260,16 @@ class LanTcpPlugin extends TcpPlugin {
@Override
public Callable<KeyAgreementConnection> listen() {
return () -> {
Socket s = ss.accept();
if (LOG.isLoggable(INFO))
LOG.info(ID.getString() + ": Incoming connection");
return new KeyAgreementConnection(
new TcpTransportConnection(LanTcpPlugin.this, s), ID);
return new Callable<KeyAgreementConnection>() {
@Override
public KeyAgreementConnection call() throws IOException {
Socket s = ss.accept();
if (LOG.isLoggable(INFO))
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
public MappingResult map(int port) {
public MappingResult map(final int port) {
if (!started.getAndSet(true)) start();
if (gateway == null) return null;
InetAddress internal = gateway.getLocalAddress();
@@ -50,7 +50,12 @@ class PortMapperImpl implements PortMapper {
succeeded = gateway.addPortMapping(port, port,
getHostAddress(internal), "TCP", "TCP");
if (succeeded) {
shutdownManager.addShutdownHook(() -> deleteMapping(port));
shutdownManager.addShutdownHook(new Runnable() {
@Override
public void run() {
deleteMapping(port);
}
});
}
String externalString = gateway.getExternalIPAddress();
if (LOG.isLoggable(INFO))
@@ -58,7 +63,9 @@ class PortMapperImpl implements PortMapper {
"External address " + scrubInetAddress(externalString));
if (externalString != null)
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);
}
return new MappingResult(internal, external, port, succeeded);
@@ -75,7 +82,11 @@ class PortMapperImpl implements PortMapper {
GatewayDiscover d = new GatewayDiscover();
try {
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);
}
gateway = d.getValidGateway();
@@ -86,7 +97,9 @@ class PortMapperImpl implements PortMapper {
gateway.deletePortMapping(port, "TCP");
if (LOG.isLoggable(INFO))
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);
}
}

View File

@@ -110,37 +110,41 @@ abstract class TcpPlugin implements DuplexPlugin {
}
protected void bind() {
ioExecutor.execute(() -> {
if (!running) return;
ServerSocket ss = null;
for (InetSocketAddress addr : getLocalSocketAddresses()) {
try {
ss = new ServerSocket();
ss.bind(addr);
break;
} catch (IOException e) {
if (LOG.isLoggable(INFO))
LOG.info("Failed to bind " + scrubSocketAddress(addr));
tryToClose(ss);
ioExecutor.execute(new Runnable() {
@Override
public void run() {
if (!running) return;
ServerSocket ss = null;
for (InetSocketAddress addr : getLocalSocketAddresses()) {
try {
ss = new ServerSocket();
ss.bind(addr);
break;
} catch (IOException e) {
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) {
ioExecutor.execute(() -> {
if (!isRunning()) return;
DuplexTransportConnection d = createConnection(p);
if (d != null) {
backoff.reset();
callback.outgoingConnectionCreated(c, d);
private void connectAndCallBack(final ContactId c,
final TransportProperties p) {
ioExecutor.execute(new Runnable() {
@Override
public void run() {
if (!isRunning()) return;
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);
return Collections.emptyList();
}
List<InetAddress> addrs = new ArrayList<>();
List<InetAddress> addrs = new ArrayList<InetAddress>();
for (NetworkInterface iface : ifaces)
addrs.addAll(Collections.list(iface.getInetAddresses()));
return addrs;

View File

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

View File

@@ -40,12 +40,9 @@ public class PropertiesModule {
@Provides
@Singleton
TransportPropertyManager getTransportPropertyManager(
LifecycleManager lifecycleManager,
ValidationManager validationManager, ContactManager contactManager,
LifecycleManager lifecycleManager, ContactManager contactManager,
TransportPropertyManagerImpl transportPropertyManager) {
lifecycleManager.registerClient(transportPropertyManager);
validationManager.registerIncomingMessageHook(CLIENT_ID,
transportPropertyManager);
contactManager.registerAddContactHook(transportPropertyManager);
contactManager.registerRemoveContactHook(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.data.BdfDictionary;
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.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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.Group;
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.MessageId;
import org.briarproject.bramble.api.sync.ValidationManager.IncomingMessageHook;
import org.briarproject.bramble.api.system.Clock;
import java.util.HashMap;
@@ -40,26 +36,23 @@ import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
@Immutable
@NotNullByDefault
class TransportPropertyManagerImpl implements TransportPropertyManager,
Client, AddContactHook, RemoveContactHook, IncomingMessageHook {
Client, AddContactHook, RemoveContactHook {
private final DatabaseComponent db;
private final ClientHelper clientHelper;
private final MetadataParser metadataParser;
private final ContactGroupFactory contactGroupFactory;
private final Clock clock;
private final Group localGroup;
@Inject
TransportPropertyManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory, Clock clock) {
ClientHelper clientHelper, ContactGroupFactory contactGroupFactory,
Clock clock) {
this.db = db;
this.clientHelper = clientHelper;
this.metadataParser = metadataParser;
this.contactGroupFactory = contactGroupFactory;
this.clock = clock;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
CLIENT_VERSION);
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID);
}
@Override
@@ -91,31 +84,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
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
public void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException {
@@ -144,9 +112,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
public Map<TransportId, TransportProperties> getLocalProperties(
Transaction txn) throws DbException {
try {
Map<TransportId, TransportProperties> local = new HashMap<>();
Map<TransportId, TransportProperties> local =
new HashMap<TransportId, TransportProperties>();
// 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
for (Entry<TransportId, LatestUpdate> e : latest.entrySet()) {
BdfList message = clientHelper.getMessageAsList(txn,
@@ -190,7 +160,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override
public Map<ContactId, TransportProperties> getRemoteProperties(
TransportId t) throws DbException {
Map<ContactId, TransportProperties> remote = new HashMap<>();
Map<ContactId, TransportProperties> remote =
new HashMap<ContactId, TransportProperties>();
Transaction txn = db.startTransaction(true);
try {
for (Contact c : db.getContacts(txn))
@@ -263,9 +234,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
long version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, localGroup.getId(), t, merged, version,
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
for (Contact c : db.getContacts(txn)) {
Group g = getContactGroup(c);
@@ -273,9 +241,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, g.getId(), t, merged, version,
true, true);
// Delete the previous update, if any
if (latest != null)
db.removeMessage(txn, latest.messageId);
}
}
db.commitTransaction(txn);
@@ -288,8 +253,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
}
private Group getContactGroup(Contact c) {
return contactGroupFactory.createContactGroup(CLIENT_ID,
CLIENT_VERSION, c);
return contactGroupFactory.createContactGroup(CLIENT_ID, c);
}
private void storeMessage(Transaction txn, GroupId g, TransportId t,
@@ -314,16 +278,21 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
return BdfList.of(t.getString(), version, p);
}
private Map<TransportId, LatestUpdate> findLatestLocal(Transaction txn)
throws DbException, FormatException {
Map<TransportId, LatestUpdate> latestUpdates = new HashMap<>();
Map<MessageId, BdfDictionary> metadata = clientHelper
.getMessageMetadataAsDictionary(txn, localGroup.getId());
private Map<TransportId, LatestUpdate> findLatest(Transaction txn,
GroupId g, boolean local) throws DbException, FormatException {
Map<TransportId, LatestUpdate> latestUpdates =
new HashMap<TransportId, LatestUpdate>();
Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g);
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue();
TransportId t = new TransportId(meta.getString("transportId"));
long version = meta.getLong("version");
latestUpdates.put(t, new LatestUpdate(e.getKey(), version));
if (meta.getBoolean("local") == local) {
TransportId t = new TransportId(meta.getString("transportId"));
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;
}
@@ -331,16 +300,19 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Nullable
private LatestUpdate findLatest(Transaction txn, GroupId g, TransportId t,
boolean local) throws DbException, FormatException {
LatestUpdate latest = null;
Map<MessageId, BdfDictionary> metadata =
clientHelper.getMessageMetadataAsDictionary(txn, g);
for (Entry<MessageId, BdfDictionary> e : metadata.entrySet()) {
BdfDictionary meta = e.getValue();
if (meta.getString("transportId").equals(t.getString())
&& 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)

View File

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

View File

@@ -42,44 +42,48 @@ class ReliabilityLayerImpl implements ReliabilityLayer, WriteHandler {
this.executor = executor;
this.clock = clock;
this.writeHandler = writeHandler;
writes = new LinkedBlockingQueue<>();
writes = new LinkedBlockingQueue<byte[]>();
}
@Override
public void start() {
SlipEncoder encoder = new SlipEncoder(this);
Sender sender = new Sender(clock, encoder);
final Sender sender = new Sender(clock, encoder);
receiver = new Receiver(clock, sender);
decoder = new SlipDecoder(receiver, Data.MAX_LENGTH);
inputStream = new ReceiverInputStream(receiver);
outputStream = new SenderOutputStream(sender);
running = true;
executor.execute(() -> {
long now = clock.currentTimeMillis();
long next = now + TICK_INTERVAL;
try {
while (running) {
byte[] b = null;
while (now < next && b == null) {
b = writes.poll(next - now, MILLISECONDS);
if (!running) return;
now = clock.currentTimeMillis();
}
if (b == null) {
sender.tick();
while (next <= now) next += TICK_INTERVAL;
} else {
if (b.length == 0) return; // Poison pill
writeHandler.handleWrite(b);
executor.execute(new Runnable() {
@Override
public void run() {
long now = clock.currentTimeMillis();
long next = now + TICK_INTERVAL;
try {
while (running) {
byte[] b = null;
while (now < next && b == null) {
b = writes.poll(next - now, MILLISECONDS);
if (!running) return;
now = clock.currentTimeMillis();
}
if (b == null) {
sender.tick();
while (next <= now) next += TICK_INTERVAL;
} 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) {
this.clock = clock;
this.writeHandler = writeHandler;
outstanding = new LinkedList<>();
outstanding = new LinkedList<Outstanding>();
}
void sendAck(long sequenceNumber, int windowSize) throws IOException {
@@ -136,7 +136,7 @@ class Sender {
if (now - o.lastTransmitted > rto) {
it.remove();
if (retransmit == null)
retransmit = new ArrayList<>();
retransmit = new ArrayList<Outstanding>();
retransmit.add(o);
// Update the retransmission timeout
rto <<= 1;

View File

@@ -54,7 +54,12 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
private static final Logger LOG =
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 Executor dbExecutor;
@@ -78,7 +83,7 @@ class DuplexOutgoingSession implements SyncSession, EventListener {
this.maxLatency = maxLatency;
this.maxIdleTime = maxIdleTime;
this.recordWriter = recordWriter;
writerTasks = new LinkedBlockingQueue<>();
writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
}
@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.GroupFactory;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import javax.annotation.concurrent.Immutable;
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
@NotNullByDefault
class GroupFactoryImpl implements GroupFactory {
@@ -28,12 +23,9 @@ class GroupFactoryImpl implements GroupFactory {
}
@Override
public Group createGroup(ClientId c, int clientVersion, byte[] descriptor) {
byte[] clientVersionBytes = new byte[INT_32_BYTES];
ByteUtils.writeUint32(clientVersion, clientVersionBytes, 0);
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
StringUtils.toUtf8(c.getString()), clientVersionBytes,
descriptor);
public Group createGroup(ClientId c, byte[] descriptor) {
byte[] hash = crypto.hash(GroupId.LABEL,
StringUtils.toUtf8(c.getString()), 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.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.MESSAGE_HEADER_LENGTH;
import static org.briarproject.bramble.api.sync.SyncConstants.PROTOCOL_VERSION;
@Immutable
@NotNullByDefault
@@ -34,9 +32,9 @@ class MessageFactoryImpl implements MessageFactory {
throw new IllegalArgumentException();
byte[] timeBytes = new byte[ByteUtils.INT_64_BYTES];
ByteUtils.writeUint64(timestamp, timeBytes, 0);
byte[] hash = crypto.hash(LABEL, new byte[] {PROTOCOL_VERSION},
g.getBytes(), timeBytes, body);
MessageId id = new MessageId(hash);
byte[] idHash =
crypto.hash(MessageId.LABEL, g.getBytes(), timeBytes, body);
MessageId id = new MessageId(idHash);
byte[] raw = new byte[MESSAGE_HEADER_LENGTH + body.length];
System.arraycopy(g.getBytes(), 0, raw, 0, UniqueId.LENGTH);
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);

View File

@@ -116,7 +116,7 @@ class RecordReaderImpl implements RecordReader {
private List<MessageId> readMessageIds() throws IOException {
if (payloadLength == 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) {
byte[] id = new byte[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 =
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 Executor dbExecutor;
@@ -66,7 +71,7 @@ class SimplexOutgoingSession implements SyncSession, EventListener {
this.maxLatency = maxLatency;
this.recordWriter = recordWriter;
outstandingQueries = new AtomicInteger(2); // One per type of record
writerTasks = new LinkedBlockingQueue<>();
writerTasks = new LinkedBlockingQueue<ThrowingRunnable<IOException>>();
}
@IoExecutor

View File

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

View File

@@ -58,14 +58,15 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
this.pluginConfig = pluginConfig;
this.transportKeyManagerFactory = transportKeyManagerFactory;
// Use a ConcurrentHashMap as a thread-safe set
activeContacts = new ConcurrentHashMap<>();
managers = new ConcurrentHashMap<>();
activeContacts = new ConcurrentHashMap<ContactId, Boolean>();
managers = new ConcurrentHashMap<TransportId, TransportKeyManager>();
}
@Override
public void startService() throws ServiceException {
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())
transports.put(f.getId(), f.getMaxLatency());
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);
dbExecutor.execute(() -> {
for (TransportKeyManager m : managers.values()) m.removeContact(c);
dbExecutor.execute(new Runnable() {
@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> unseen = new ArrayList<>(seen.length);
List<Long> unseen = new ArrayList<Long>(seen.length);
for (int i = 0; i < seen.length; i++)
if (!seen[i]) unseen.add(base + i);
return unseen;
@@ -69,8 +69,8 @@ class ReorderingWindow {
return new Change(added, removed);
}
// Record the elements that will be added and removed
List<Long> added = new ArrayList<>(slide);
List<Long> removed = new ArrayList<>(slide);
List<Long> added = new ArrayList<Long>(slide);
List<Long> removed = new ArrayList<Long>(slide);
for (int i = 0; i < slide; i++) {
if (!seen[i]) removed.add(base + i);
added.add(base + seen.length + i);

View File

@@ -1,6 +1,6 @@
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.DatabaseExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -20,18 +20,17 @@ class TransportKeyManagerFactoryImpl implements
TransportKeyManagerFactory {
private final DatabaseComponent db;
private final TransportCrypto transportCrypto;
private final CryptoComponent crypto;
private final Executor dbExecutor;
private final ScheduledExecutorService scheduler;
private final Clock clock;
@Inject
TransportKeyManagerFactoryImpl(DatabaseComponent db,
TransportCrypto transportCrypto,
TransportKeyManagerFactoryImpl(DatabaseComponent db, CryptoComponent crypto,
@DatabaseExecutor Executor dbExecutor,
@Scheduler ScheduledExecutorService scheduler, Clock clock) {
this.db = db;
this.transportCrypto = transportCrypto;
this.crypto = crypto;
this.dbExecutor = dbExecutor;
this.scheduler = scheduler;
this.clock = clock;
@@ -40,8 +39,8 @@ class TransportKeyManagerFactoryImpl implements
@Override
public TransportKeyManager createTransportKeyManager(
TransportId transportId, long maxLatency) {
return new TransportKeyManagerImpl(db, transportCrypto, dbExecutor,
scheduler, clock, transportId, maxLatency);
return new TransportKeyManagerImpl(db, crypto, dbExecutor, scheduler,
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.contact.ContactId;
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.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
@@ -41,7 +41,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
Logger.getLogger(TransportKeyManagerImpl.class.getName());
private final DatabaseComponent db;
private final TransportCrypto transportCrypto;
private final CryptoComponent crypto;
private final Executor dbExecutor;
private final ScheduledExecutorService scheduler;
private final Clock clock;
@@ -54,21 +54,20 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final Map<ContactId, MutableOutgoingKeys> outContexts;
private final Map<ContactId, MutableTransportKeys> keys;
TransportKeyManagerImpl(DatabaseComponent db,
TransportCrypto transportCrypto, Executor dbExecutor,
@Scheduler ScheduledExecutorService scheduler, Clock clock,
TransportId transportId, long maxLatency) {
TransportKeyManagerImpl(DatabaseComponent db, CryptoComponent crypto,
Executor dbExecutor, @Scheduler ScheduledExecutorService scheduler,
Clock clock, TransportId transportId, long maxLatency) {
this.db = db;
this.transportCrypto = transportCrypto;
this.crypto = crypto;
this.dbExecutor = dbExecutor;
this.scheduler = scheduler;
this.clock = clock;
this.transportId = transportId;
rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
lock = new ReentrantLock();
inContexts = new HashMap<>();
outContexts = new HashMap<>();
keys = new HashMap<>();
inContexts = new HashMap<Bytes, TagContext>();
outContexts = new HashMap<ContactId, MutableOutgoingKeys>();
keys = new HashMap<ContactId, MutableTransportKeys>();
}
@Override
@@ -100,8 +99,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (Entry<ContactId, TransportKeys> e : keys.entrySet()) {
ContactId c = e.getKey();
TransportKeys k = e.getValue();
TransportKeys k1 =
transportCrypto.rotateTransportKeys(k, rotationPeriod);
TransportKeys k1 = crypto.rotateTransportKeys(k, rotationPeriod);
if (k1.getRotationPeriod() > k.getRotationPeriod())
rotationResult.rotated.put(c, k1);
rotationResult.current.put(c, k1);
@@ -129,29 +127,39 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
byte[] tag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
inContexts.put(new Bytes(tag), tagCtx);
}
}
private void scheduleKeyRotation(long now) {
Runnable task = new Runnable() {
@Override
public void run() {
rotateKeys();
}
};
long delay = rotationPeriodLength - now % rotationPeriodLength;
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
scheduler.schedule(task, delay, MILLISECONDS);
}
private void rotateKeys() {
dbExecutor.execute(() -> {
try {
Transaction txn = db.startTransaction(false);
dbExecutor.execute(new Runnable() {
@Override
public void run() {
try {
rotateKeys(txn);
db.commitTransaction(txn);
} finally {
db.endTransaction(txn);
Transaction txn = db.startTransaction(false);
try {
rotateKeys(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
long rotationPeriod = timestamp / rotationPeriodLength;
// Derive the transport keys
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, rotationPeriod, alice);
TransportKeys k = crypto.deriveTransportKeys(transportId, master,
rotationPeriod, alice);
// Rotate the keys to the current rotation period if necessary
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
k = transportCrypto.rotateTransportKeys(k, rotationPeriod);
k = crypto.rotateTransportKeys(k, rotationPeriod);
// Initialise mutable state for the contact
addKeys(c, new MutableTransportKeys(k));
// 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
for (long streamNumber : change.getAdded()) {
byte[] addTag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(addTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
inContexts.put(new Bytes(addTag), new TagContext(
tagCtx.contactId, inKeys, streamNumber));
}
@@ -245,7 +253,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue;
byte[] removeTag = new byte[TAG_LENGTH];
transportCrypto.encodeTag(removeTag, inKeys.getTagKey(),
crypto.encodeTag(removeTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
inContexts.remove(new Bytes(removeTag));
}
@@ -264,7 +272,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
lock.lock();
try {
// 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())
snapshot.put(e.getKey(), e.getValue().snapshot());
RotationResult rotationResult = rotateKeys(snapshot, now);
@@ -302,8 +311,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final Map<ContactId, TransportKeys> current, rotated;
private RotationResult() {
current = new HashMap<>();
rotated = new HashMap<>();
current = new HashMap<ContactId, TransportKeys>();
rotated = new HashMap<ContactId, TransportKeys>();
}
}
}

View File

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

View File

@@ -83,9 +83,9 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
@Test(expected = InvalidMessageException.class)
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
Message invalidMessage = context.mock(Message.class);
final Message invalidMessage = context.mock(Message.class);
context.checking(new Expectations() {{
oneOf(invalidMessage).getTimestamp();
@@ -101,8 +101,8 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
@Test
public void testAcceptsMinLengthMessage() throws Exception {
byte[] shortRaw = new byte[MESSAGE_HEADER_LENGTH + 1];
Message shortMessage =
final byte[] shortRaw = new byte[MESSAGE_HEADER_LENGTH + 1];
final Message shortMessage =
new Message(messageId, groupId, timestamp, shortRaw);
context.checking(new Expectations() {{

View File

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

View File

@@ -46,10 +46,10 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test
public void testAddContact() throws Exception {
SecretKey master = getSecretKey();
long timestamp = 42;
boolean alice = true;
Transaction txn = new Transaction(null, false);
final SecretKey master = getSecretKey();
final long timestamp = 42;
final boolean alice = true;
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
@@ -64,13 +64,14 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
oneOf(db).endTransaction(txn);
}});
assertEquals(contactId, contactManager.addContact(remote, local,
master, timestamp, alice, verified, active));
assertEquals(contactId, contactManager
.addContact(remote, local, master, timestamp, alice, verified,
active));
}
@Test
public void testGetContact() throws Exception {
Transaction txn = new Transaction(null, true);
final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
@@ -85,8 +86,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test
public void testGetContactByAuthor() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = Collections.singleton(contact);
final Transaction txn = new Transaction(null, true);
final Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
@@ -101,7 +102,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownAuthor() throws Exception {
Transaction txn = new Transaction(null, true);
final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
@@ -115,8 +116,8 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownLocalAuthor() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = Collections.singleton(contact);
final Transaction txn = new Transaction(null, true);
final Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
@@ -131,9 +132,10 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test
public void testActiveContacts() throws Exception {
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));
Transaction txn = new Transaction(null, true);
final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
will(returnValue(txn));
@@ -148,7 +150,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test
public void testRemoveContact() throws Exception {
Transaction txn = new Transaction(null, false);
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
@@ -164,7 +166,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test
public void testSetContactActive() throws Exception {
Transaction txn = new Transaction(null, false);
final Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).setContactActive(txn, contactId, active);
}});
@@ -174,7 +176,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
@Test
public void testContactExists() throws Exception {
Transaction txn = new Transaction(null, true);
final Transaction txn = new Transaction(null, true);
context.checking(new Expectations() {{
oneOf(db).startTransaction(true);
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;
import net.i2p.crypto.eddsa.EdDSASecurityProvider;
import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.spongycastle.asn1.sec.SECNamedCurves;
import org.spongycastle.asn1.teletrust.TeleTrusTNamedCurves;
import org.spongycastle.asn1.x9.X9ECParameters;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.BasicAgreement;
import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
import org.spongycastle.crypto.generators.ECKeyPairGenerator;
import org.spongycastle.crypto.params.ECDomainParameters;
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.signers.DSADigestSigner;
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.ECPoint;
import org.spongycastle.math.ec.MontgomeryLadderMultiplier;
import org.whispersystems.curve25519.Curve25519;
import org.whispersystems.curve25519.Curve25519KeyPair;
import java.math.BigInteger;
import java.security.GeneralSecurityException;
import java.security.KeyPair;
import java.security.Provider;
import java.security.SecureRandom;
import java.security.Signature;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
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
public class EllipticCurvePerformanceTest {
@@ -49,9 +37,8 @@ public class EllipticCurvePerformanceTest {
"secp256k1", "secp256r1", "secp384r1", "secp521r1");
private static final List<String> BRAINPOOL_NAMES = Arrays.asList(
"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) {
ECDomainParameters params =
convertParams(SECNamedCurves.getByName(name));
@@ -64,32 +51,43 @@ public class EllipticCurvePerformanceTest {
runTest(name + " default", params);
runTest(name + " constant", constantTime(params));
}
runTest("ours", PARAMETERS);
runCurve25519Test();
runEd25519Test();
runTest("ours", EllipticCurveConstants.PARAMETERS);
}
private static void runTest(String name, ECDomainParameters params) {
// Generate two key pairs using the given parameters
ECKeyGenerationParameters generatorParams =
new ECKeyGenerationParameters(params, random);
ECKeyPairGenerator generator = new ECKeyPairGenerator();
generator.init(new ECKeyGenerationParameters(params, random));
generator.init(generatorParams);
AsymmetricCipherKeyPair keyPair1 = generator.generateKeyPair();
ECPublicKeyParameters public1 =
(ECPublicKeyParameters) keyPair1.getPublic();
ECPrivateKeyParameters private1 =
(ECPrivateKeyParameters) keyPair1.getPrivate();
AsymmetricCipherKeyPair keyPair2 = generator.generateKeyPair();
// Time some ECDH and ECDHC key agreements
long agreementMedian = runAgreementTest(keyPair1, keyPair2, false);
long agreementWithCofactorMedian =
runAgreementTest(keyPair1, keyPair2, true);
ECPublicKeyParameters public2 =
(ECPublicKeyParameters) keyPair2.getPublic();
// Time some ECDH key agreements
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
List<Long> samples = new ArrayList<>();
List<byte[]> signatures = new ArrayList<>();
List<byte[]> signatures = new ArrayList<byte[]>();
samples.clear();
for (int i = 0; i < SAMPLES; i++) {
Digest digest = new Blake2sDigest();
DSAKCalculator calculator = new HMacDSAKCalculator(digest);
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
calculator), digest);
long start = System.nanoTime();
signer.init(true,
new ParametersWithRandom(keyPair1.getPrivate(), random));
signer.init(true, new ParametersWithRandom(private1, random));
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
signatures.add(signer.generateSignature());
samples.add(System.nanoTime() - start);
@@ -103,83 +101,17 @@ public class EllipticCurvePerformanceTest {
DSADigestSigner signer = new DSADigestSigner(new ECDSASigner(
calculator), digest);
long start = System.nanoTime();
signer.init(false, keyPair1.getPublic());
signer.init(false, public1);
signer.update(new byte[BYTES_TO_SIGN], 0, BYTES_TO_SIGN);
if (!signer.verifySignature(signatures.get(i)))
throw new AssertionError();
samples.add(System.nanoTime() - start);
}
long verificationMedian = median(samples);
System.out.println(String.format("%s: %,d %,d %,d %,d", name,
agreementMedian, agreementWithCofactorMedian,
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));
System.out.println(name + ": "
+ agreementMedian + " "
+ signatureMedian + " "
+ verificationMedian);
}
private static long median(List<Long> list) {

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