Compare commits

..

5 Commits

Author SHA1 Message Date
akwizgran
707066f1a1 Add new module to integration tests. 2018-12-06 18:02:48 +00:00
akwizgran
65e5dc266f Hook up MessagingManager to MessageInputStreamFactory. 2018-12-06 17:46:14 +00:00
akwizgran
3487a7cfee Implement MessageInputStreamFactory interface. 2018-12-06 14:05:36 +00:00
akwizgran
eea453fc5f Add placeholder BlockSource implementation to DB. 2018-12-06 13:55:49 +00:00
akwizgran
c6f460a936 Add input stream that fetches blocks from the DB. 2018-12-06 11:34:40 +00:00
411 changed files with 5641 additions and 12399 deletions

View File

@@ -11,8 +11,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 26 targetSdkVersion 26
versionCode 10106 versionCode 10105
versionName "1.1.6" versionName "1.1.5"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
@@ -30,8 +30,8 @@ configurations {
dependencies { dependencies {
implementation project(path: ':bramble-core', configuration: 'default') implementation project(path: ':bramble-core', configuration: 'default')
tor 'org.briarproject:tor-android:0.3.5.8@zip' tor 'org.briarproject:tor-android:0.3.4.8@zip'
tor 'org.briarproject:obfs4proxy-android:0.0.9@zip' tor 'org.briarproject:obfs4proxy-android:0.0.7@zip'
annotationProcessor 'com.google.dagger:dagger-compiler:2.19' annotationProcessor 'com.google.dagger:dagger-compiler:2.19'

View File

@@ -12,15 +12,11 @@ import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.util.IoUtils; import org.briarproject.bramble.util.IoUtils;
import java.io.File; import java.io.File;
import java.util.HashSet;
import java.util.Set;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT;
class AndroidAccountManager extends AccountManagerImpl class AndroidAccountManager extends AccountManagerImpl
implements AccountManager { implements AccountManager {
@@ -93,42 +89,20 @@ class AndroidAccountManager extends AccountManagerImpl
LOG.warning("Could not clear shared preferences"); LOG.warning("Could not clear shared preferences");
} }
// Delete files, except lib and shared_prefs directories // Delete files, except lib and shared_prefs directories
Set<File> files = new HashSet<>();
File dataDir = new File(appContext.getApplicationInfo().dataDir); File dataDir = new File(appContext.getApplicationInfo().dataDir);
@Nullable File[] children = dataDir.listFiles();
File[] fileArray = dataDir.listFiles(); if (children == null) {
if (fileArray == null) {
LOG.warning("Could not list files in app data dir"); LOG.warning("Could not list files in app data dir");
} else { } else {
for (File file : fileArray) { for (File child : children) {
String name = file.getName(); String name = child.getName();
if (!name.equals("lib") && !name.equals("shared_prefs")) { if (!name.equals("lib") && !name.equals("shared_prefs")) {
files.add(file); IoUtils.deleteFileOrDir(child);
} }
} }
} }
files.add(appContext.getFilesDir());
files.add(appContext.getCacheDir());
addIfNotNull(files, appContext.getExternalCacheDir());
if (SDK_INT >= 19) {
for (File file : appContext.getExternalCacheDirs()) {
addIfNotNull(files, file);
}
}
if (SDK_INT >= 21) {
for (File file : appContext.getExternalMediaDirs()) {
addIfNotNull(files, file);
}
}
for (File file : files) {
IoUtils.deleteFileOrDir(file);
}
// Recreate the cache dir as some OpenGL drivers expect it to exist // Recreate the cache dir as some OpenGL drivers expect it to exist
if (!new File(dataDir, "cache").mkdirs()) if (!new File(dataDir, "cache").mkdir())
LOG.warning("Could not recreate cache dir"); LOG.warning("Could not recreate cache dir");
} }
private void addIfNotNull(Set<File> files, @Nullable File file) {
if (file != null) files.add(file);
}
} }

View File

@@ -19,7 +19,9 @@ import javax.inject.Inject;
import static android.content.Intent.ACTION_BATTERY_CHANGED; import static android.content.Intent.ACTION_BATTERY_CHANGED;
import static android.content.Intent.ACTION_POWER_CONNECTED; import static android.content.Intent.ACTION_POWER_CONNECTED;
import static android.content.Intent.ACTION_POWER_DISCONNECTED; import static android.content.Intent.ACTION_POWER_DISCONNECTED;
import static android.os.BatteryManager.EXTRA_PLUGGED; import static android.os.BatteryManager.BATTERY_STATUS_CHARGING;
import static android.os.BatteryManager.BATTERY_STATUS_FULL;
import static android.os.BatteryManager.EXTRA_STATUS;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
@@ -46,8 +48,9 @@ class AndroidBatteryManager implements BatteryManager, Service {
IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED); IntentFilter filter = new IntentFilter(ACTION_BATTERY_CHANGED);
Intent i = appContext.registerReceiver(null, filter); Intent i = appContext.registerReceiver(null, filter);
if (i == null) return false; if (i == null) return false;
int status = i.getIntExtra(EXTRA_PLUGGED, 0); int status = i.getIntExtra(EXTRA_STATUS, -1);
return status != 0; return status == BATTERY_STATUS_CHARGING ||
status == BATTERY_STATUS_FULL;
} }
@Override @Override

View File

@@ -32,6 +32,9 @@ import static java.util.concurrent.TimeUnit.MINUTES;
@ParametersNotNullByDefault @ParametersNotNullByDefault
class AndroidTorPlugin extends TorPlugin { class AndroidTorPlugin extends TorPlugin {
// This tag may prevent Huawei's power manager from killing us
private static final String WAKE_LOCK_TAG = "LocationManagerService";
private final Context appContext; private final Context appContext;
private final RenewableWakeLock wakeLock; private final RenewableWakeLock wakeLock;
@@ -52,7 +55,7 @@ class AndroidTorPlugin extends TorPlugin {
appContext.getSystemService(POWER_SERVICE); appContext.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError(); if (pm == null) throw new AssertionError();
wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK, wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK,
getWakeLockTag(), 1, MINUTES); WAKE_LOCK_TAG, 1, MINUTES);
} }
@Override @Override
@@ -84,17 +87,4 @@ class AndroidTorPlugin extends TorPlugin {
super.stop(); super.stop();
wakeLock.release(); wakeLock.release();
} }
private String getWakeLockTag() {
PackageManager pm = appContext.getPackageManager();
for (PackageInfo info : pm.getInstalledPackages(0)) {
String name = info.packageName.toLowerCase();
if (name.startsWith("com.huawei.powergenie")) {
return "LocationManagerService";
} else if (name.startsWith("com.evenwell.powermonitor")) {
return "AudioIn";
}
}
return getClass().getSimpleName();
}
} }

View File

@@ -1,13 +1,10 @@
package org.briarproject.bramble.system; package org.briarproject.bramble.system;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.LocationUtils; import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider; import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.SecureRandomProvider; import org.briarproject.bramble.api.system.SecureRandomProvider;
import java.util.concurrent.Executor;
import javax.inject.Singleton; import javax.inject.Singleton;
import dagger.Module; import dagger.Module;
@@ -35,13 +32,6 @@ public class AndroidSystemModule {
return androidExecutor; return androidExecutor;
} }
@Provides
@Singleton
@EventExecutor
Executor provideEventExecutor(AndroidExecutor androidExecutor) {
return androidExecutor::runOnUiThread;
}
@Provides @Provides
@Singleton @Singleton
ResourceProvider provideResourceProvider(AndroidResourceProvider provider) { ResourceProvider provideResourceProvider(AndroidResourceProvider provider) {

View File

@@ -112,8 +112,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
// Other directories should be deleted // Other directories should be deleted
File potatoDir = new File(testDir, ".potato"); File potatoDir = new File(testDir, ".potato");
File potatoFile = new File(potatoDir, "file"); File potatoFile = new File(potatoDir, "file");
File filesDir = new File(testDir, "filesDir");
File externalCacheDir = new File(testDir, "externalCacheDir");
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(prefs).edit(); oneOf(prefs).edit();
@@ -130,12 +128,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
oneOf(app).getApplicationInfo(); oneOf(app).getApplicationInfo();
will(returnValue(applicationInfo)); will(returnValue(applicationInfo));
oneOf(app).getFilesDir();
will(returnValue(filesDir));
oneOf(app).getCacheDir();
will(returnValue(cacheDir));
oneOf(app).getExternalCacheDir();
will(returnValue(externalCacheDir));
}}); }});
assertTrue(dbDir.mkdirs()); assertTrue(dbDir.mkdirs());
@@ -148,8 +140,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertTrue(cacheFile.createNewFile()); assertTrue(cacheFile.createNewFile());
assertTrue(potatoDir.mkdirs()); assertTrue(potatoDir.mkdirs());
assertTrue(potatoFile.createNewFile()); assertTrue(potatoFile.createNewFile());
assertTrue(filesDir.mkdirs());
assertTrue(externalCacheDir.mkdirs());
accountManager.deleteAccount(); accountManager.deleteAccount();
@@ -163,8 +153,6 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
assertFalse(cacheFile.exists()); assertFalse(cacheFile.exists());
assertFalse(potatoDir.exists()); assertFalse(potatoDir.exists());
assertFalse(potatoFile.exists()); assertFalse(potatoFile.exists());
assertFalse(filesDir.exists());
assertFalse(externalCacheDir.exists());
} }
@After @After

View File

@@ -1,44 +1,46 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.android.tools.analytics-library:protos:26.3.2:protos-26.3.2.jar:50238fb4298b297217b184b9cd93c14f83536fcee829eb0ca850bdb5b53251f0', 'com.android.tools.analytics-library:protos:26.2.1:protos-26.2.1.jar:2f371f5b1f551e85ab08be4d6a2873471b3d44afd1ebf6aa3298f3b796bf691f',
'com.android.tools.analytics-library:shared:26.3.2:shared-26.3.2.jar:ddd80dcf21905018b7c0583ba72b7282f446084d4952422609a09fbf8237ef71', 'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a',
'com.android.tools.analytics-library:tracker:26.3.2:tracker-26.3.2.jar:28c575d2d1af003e96d7b375a668ee10b2673a2dd0f6438750aa8c3b42e7d0ad', 'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75',
'com.android.tools.build:apksig:3.3.2:apksig-3.3.2.jar:84c4aaa20127c6c1fe6bdd334b3f5df71f54ad080be9029c8a10f43b6a908acd', 'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e',
'com.android.tools.build:apkzlib:3.3.2:apkzlib-3.3.2.jar:d34e523278e5dff565eba3ef3c089d515b2b5cc7b47dc77e2f3465e5e47176ac', 'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5',
'com.android.tools.build:builder-model:3.3.2:builder-model-3.3.2.jar:055e3db0ecee9e06b9f024034999a29cd92cb1885207b37542126bd8bcc57f46', 'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa',
'com.android.tools.build:builder-test-api:3.3.2:builder-test-api-3.3.2.jar:0b2e4cd7615bbcad14a3c91fe45ae26693508d06e40ba06c5968b8bc24416618', 'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff',
'com.android.tools.build:builder:3.3.2:builder-3.3.2.jar:65649704da7aef0487235fd326f0f2e99ed5cf958e80f204496e6e08a42bd9f5', 'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be',
'com.android.tools.build:gradle-api:3.3.2:gradle-api-3.3.2.jar:3cbd47e41bb70330dd72ec2c9fe51e6173554b484a03829b5a2de9e00841e040', 'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83',
'com.android.tools.build:manifest-merger:26.3.2:manifest-merger-26.3.2.jar:05c4a6d8b02fb9f08744876477d0a68547c03a8a9069b1f086684fa04af97c33', 'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d',
'com.android.tools.ddms:ddmlib:26.3.2:ddmlib-26.3.2.jar:d248da8a563d6e46d2c7ebbf371a4877e00510f4ca763c0bb272d5a281bf8b85', 'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6',
'com.android.tools.external.com-intellij:intellij-core:26.3.2:intellij-core-26.3.2.jar:6c5ecc968230e9f4dcd0fef28885379feace1f0cd8130de6f61d649c86139bf3', 'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b',
'com.android.tools.external.com-intellij:kotlin-compiler:26.3.2:kotlin-compiler-26.3.2.jar:1007d9b07ccb49cd8eaf30fda10ed4681d4714f2f9ab2ecda39b4e539cc51bbe', 'com.android.tools.external.com-intellij:intellij-core:26.2.1:intellij-core-26.2.1.jar:4925ad1892c2687cb1a63427d440ef519c8c59215fefe0dc5d541d5d411fcafe',
'com.android.tools.external.org-jetbrains:uast:26.3.2:uast-26.3.2.jar:5d1833e562ea4f38a89708dfde695f0a162cbd39d003d3dde818c3fdc2b05317', 'com.android.tools.external.com-intellij:kotlin-compiler:26.2.1:kotlin-compiler-26.2.1.jar:daa064fd708f340ee25fb9823c4c74104ac77f1370b76d907eb9ae6daec0a2ae',
'com.android.tools.layoutlib:layoutlib-api:26.3.2:layoutlib-api-26.3.2.jar:d7e61e874ab95f5c350dd38b6a95b5c9dbe0083a02001884264cdb390cb255b8', 'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b',
'com.android.tools.lint:lint-api:26.3.2:lint-api-26.3.2.jar:5867dfd7fb4a4e161a816a5d29d045f9b542d34594c00a1efec46fb4cd0e1033', 'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade',
'com.android.tools.lint:lint-checks:26.3.2:lint-checks-26.3.2.jar:4b163b9c93790d2771e92ba8de58a0d9e0671ffcf2ccef3cf496efd442e27517', 'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e',
'com.android.tools.lint:lint-gradle-api:26.3.2:lint-gradle-api-26.3.2.jar:54cb282e0c054f9bed3f51302ce08b003c8ab7961dfd5a4f6de26c23cc23062f', 'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6',
'com.android.tools.lint:lint-gradle:26.3.2:lint-gradle-26.3.2.jar:bb139615f4ce97d42cc394b9389b49b76a6eb85be6785a5d272991543b519013', 'com.android.tools.lint:lint-gradle-api:26.2.1:lint-gradle-api-26.2.1.jar:2283e7af32e301565f2a797e531f0fc8c648077d457afb3ffdddbee638976c2f',
'com.android.tools.lint:lint:26.3.2:lint-26.3.2.jar:ef7b369f8a56a92ccb0f4c1c357666b9339e4a711a9d84747d446441746cfe4e', 'com.android.tools.lint:lint-gradle:26.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb',
'com.android.tools:annotations:26.3.2:annotations-26.3.2.jar:5bcce8e98b6a2f5ccf13ebcefd8f734e0b35f8b19e456575665631442ce1f7a1', 'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877',
'com.android.tools:common:26.3.2:common-26.3.2.jar:d9f8e7f0669e9a701568e3db6a87c89cf12d8fa6811c9991e969f950215ecfac', 'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5',
'com.android.tools:dvlib:26.3.2:dvlib-26.3.2.jar:d84aad56161c7773579303d69714ded6897c64c6ddfd7d456e453231e4dfe811', 'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24',
'com.android.tools:repository:26.3.2:repository-26.3.2.jar:da611eeb06e9ab8750d25b9e2901e10db8e5ec6304eb4c8b7103d39e0921ea40', 'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5',
'com.android.tools:sdk-common:26.3.2:sdk-common-26.3.2.jar:82823a3bf25e64fac33a286490f0cf5ac50c2cdb3c540149b030896bb44bf96c', 'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa',
'com.android.tools:sdklib:26.3.2:sdklib-26.3.2.jar:424d15492af67321900963238646d27495ab60de2a5b19e6a416963bc5d6932b', 'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1',
'com.android.tools:sdk-common:26.2.1:sdk-common-26.2.1.jar:759d4b292ca69a35cf961fca377b54158fc6c88108978006999442e80a011cf4',
'com.android.tools:sdklib:26.2.1:sdklib-26.2.1.jar:248df7ad5eac4aeb6f96c394c76760de4b7b89ac056e54d0c21a739368b91b45',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed', 'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825', 'com.google.code.gson:gson:2.8.0:gson-2.8.0.jar:c6221763bd79c4f1c3dc7f750b5f29a0bb38b367b81314c4f71896e340c40825',
'com.google.dagger:dagger-compiler:2.19:dagger-compiler-2.19.jar:27a4b202a2de908182edb261f8c0a264e08e5e4733d7514bc7fbf0d31da5c0fc', 'com.google.dagger:dagger-compiler:2.19:dagger-compiler-2.19.jar:27a4b202a2de908182edb261f8c0a264e08e5e4733d7514bc7fbf0d31da5c0fc',
'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768', 'com.google.dagger:dagger-producers:2.19:dagger-producers-2.19.jar:a17663abe0fc38b676026950907d4c5f5e2bf338375415861eaff6e3bdb0b768',
'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1', 'com.google.dagger:dagger-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1',
'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939', 'com.google.dagger:dagger:2.19:dagger-2.19.jar:514b6f1e0727c6572e1d65cb27e4ae668b7aeaeb93a29515182965265b609939',
'com.google.errorprone:error_prone_annotations:2.0.18:error_prone_annotations-2.0.18.jar:cb4cfad870bf563a07199f3ebea5763f0dec440fcda0b318640b1feaa788656b',
'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8', 'com.google.errorprone:error_prone_annotations:2.1.3:error_prone_annotations-2.1.3.jar:03d0329547c13da9e17c634d1049ea2ead093925e290567e1a364fd6b1fc7ff8',
'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30', 'com.google.errorprone:javac-shaded:9-dev-r4023-3:javac-shaded-9-dev-r4023-3.jar:65bfccf60986c47fbc17c9ebab0be626afc41741e0a6ec7109e0768817a36f30',
'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e', 'com.google.googlejavaformat:google-java-format:1.5:google-java-format-1.5.jar:aa19ad7850fb85178aa22f2fddb163b84d6ce4d0035872f30d4408195ca1144e',
'com.google.guava:guava:23.0:guava-23.0.jar:7baa80df284117e5b945b19b98d367a85ea7b7801bd358ff657946c3bd1b6596',
'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9', 'com.google.guava:guava:25.0-jre:guava-25.0-jre.jar:3fd4341776428c7e0e5c18a7c10de129475b69ab9d30aeafbb5c277bb6074fa9',
'com.google.guava:guava:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c',
'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f', 'com.google.j2objc:j2objc-annotations:1.1:j2objc-annotations-1.1.jar:40ceb7157feb263949e0f503fe5f71689333a621021aa20ce0d0acee3badaa0f',
'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd', 'com.google.jimfs:jimfs:1.1:jimfs-1.1.jar:c4828e28d7c0a930af9387510b3bada7daa5c04d7c25a75c7b8b081f1c257ddd',
'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4', 'com.google.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
@@ -66,22 +68,21 @@ dependencyVerification {
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:obfs4proxy-android:0.0.9:obfs4proxy-android-0.0.9.zip:9b7e9181535ea8d8bbe8ae6338e08cf4c5fc1e357a779393e0ce49586d459ae0', 'org.briarproject:obfs4proxy-android:0.0.7:obfs4proxy-android-0.0.7.zip:abdfb5d889d848de9bf214f9276abbf454808a505b870819eccc9a9e985bf617',
'org.briarproject:tor-android:0.3.5.8:tor-android-0.3.5.8.zip:42a13a6f185be1a62f42e3f30ce66a3c099ac5ec890a65e7593111b65b44a54a', 'org.briarproject:tor-android:0.3.4.8:tor-android-0.3.4.8.zip:989a0352d9d8d8172cd6c2137654e165e5d2beb10ed1211bab3814e224ad1926',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a', 'org.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
'org.codehaus.groovy:groovy-all:2.4.15:groovy-all-2.4.15.jar:51d6c4e71782e85674239189499854359d380fb75e1a703756e3aaa5b98a5af0',
'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d', 'org.codehaus.mojo:animal-sniffer-annotations:1.14:animal-sniffer-annotations-1.14.jar:2068320bd6bad744c3673ab048f67e30bef8f518996fa380033556600669905d',
'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa', 'org.glassfish.jaxb:jaxb-core:2.2.11:jaxb-core-2.2.11.jar:37bcaee8ebb04362c8352a5bf6221b86967ecdab5164c696b10b9a2bb587b2aa',
'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70', 'org.glassfish.jaxb:jaxb-runtime:2.2.11:jaxb-runtime-2.2.11.jar:a874f2351cfba8e2946be3002d10c18a6da8f21b52ba2acf52f2b85d5520ed70',
'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea', 'org.glassfish.jaxb:txw2:2.2.11:txw2-2.2.11.jar:272a3ccad45a4511351920cd2a8633c53cab8d5220c7a92954da5526bb5eafea',
'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9', '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.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jetbrains.kotlin:kotlin-reflect:1.3.21:kotlin-reflect-1.3.21.jar:a3065c822633191e0a3e3ee12a29bec234fc4b2864a6bb87ef48cce3e9e0c26a', 'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21:kotlin-stdlib-common-1.3.21.jar:cea61f7b611895e64f58569a9757fc0ab0d582f107211e1930e0ce2a0add52a7', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21:kotlin-stdlib-jdk7-1.3.21.jar:a87875604fd42140da6938ae4d35ee61081f4482536efc6d2615b8b626a198af', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21:kotlin-stdlib-jdk8-1.3.21.jar:5823ed66ac122a1c55442ebca5a209a843ccd87f562edc31a787f3d2e47f74d4', 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
'org.jetbrains.kotlin:kotlin-stdlib:1.3.21:kotlin-stdlib-1.3.21.jar:38ba2370d9f06f50433e06b2ca775b94473c2e2785f410926079ab793c72b034', 'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7', 'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478', 'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',

View File

@@ -18,10 +18,11 @@ public interface ContactGroupFactory {
* Creates a group for the given client to share with the given contact. * Creates a group for the given client to share with the given contact.
*/ */
Group createContactGroup(ClientId clientId, int majorVersion, Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact, AuthorId local); Contact contact);
/** /**
* Creates a group for the given client to share between the given authors. * Creates a group for the given client to share between the given authors
* identified by their AuthorIds.
*/ */
Group createContactGroup(ClientId clientId, int majorVersion, Group createContactGroup(ClientId clientId, int majorVersion,
AuthorId authorId1, AuthorId authorId2); AuthorId authorId1, AuthorId authorId2);

View File

@@ -1,13 +1,13 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.util.StringUtils.toUtf8; import static org.briarproject.bramble.util.StringUtils.toUtf8;
@Immutable @Immutable
@@ -16,28 +16,24 @@ public class Contact {
private final ContactId id; private final ContactId id;
private final Author author; private final Author author;
private final AuthorId localAuthorId;
@Nullable @Nullable
private final String alias; private final String alias;
@Nullable private final boolean verified, active;
private final byte[] handshakePublicKey;
private final boolean verified;
public Contact(ContactId id, Author author, @Nullable String alias, public Contact(ContactId id, Author author, AuthorId localAuthorId,
@Nullable byte[] handshakePublicKey, boolean verified) { @Nullable String alias, boolean verified, boolean active) {
if (alias != null) { if (alias != null) {
int aliasLength = toUtf8(alias).length; int aliasLength = toUtf8(alias).length;
if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH) if (aliasLength == 0 || aliasLength > MAX_AUTHOR_NAME_LENGTH)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
if (handshakePublicKey != null && (handshakePublicKey.length == 0 ||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH)) {
throw new IllegalArgumentException();
}
this.id = id; this.id = id;
this.author = author; this.author = author;
this.localAuthorId = localAuthorId;
this.alias = alias; this.alias = alias;
this.handshakePublicKey = handshakePublicKey;
this.verified = verified; this.verified = verified;
this.active = active;
} }
public ContactId getId() { public ContactId getId() {
@@ -48,20 +44,23 @@ public class Contact {
return author; return author;
} }
public AuthorId getLocalAuthorId() {
return localAuthorId;
}
@Nullable @Nullable
public String getAlias() { public String getAlias() {
return alias; return alias;
} }
@Nullable
public byte[] getHandshakePublicKey() {
return handshakePublicKey;
}
public boolean isVerified() { public boolean isVerified() {
return verified; return verified;
} }
public boolean isActive() {
return active;
}
@Override @Override
public int hashCode() { public int hashCode() {
return id.hashCode(); return id.hashCode();

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface ContactExchangeListener {
void contactExchangeSucceeded(Author remoteAuthor);
/**
* The exchange failed because the contact already exists.
*/
void duplicateContact(Author remoteAuthor);
/**
* A general failure.
*/
void contactExchangeFailed();
}

View File

@@ -18,30 +18,31 @@ public interface ContactExchangeTask {
byte PROTOCOL_VERSION = 1; byte PROTOCOL_VERSION = 1;
/** /**
* Label for deriving Alice's header key from the master key. * Label for deriving Alice's header key from the master secret.
*/ */
String ALICE_KEY_LABEL = String ALICE_KEY_LABEL =
"org.briarproject.bramble.contact/ALICE_HEADER_KEY"; "org.briarproject.bramble.contact/ALICE_HEADER_KEY";
/** /**
* Label for deriving Bob's header key from the master key. * Label for deriving Bob's header key from the master secret.
*/ */
String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY"; String BOB_KEY_LABEL = "org.briarproject.bramble.contact/BOB_HEADER_KEY";
/** /**
* Label for deriving Alice's key binding nonce from the master key. * Label for deriving Alice's key binding nonce from the master secret.
*/ */
String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE"; String ALICE_NONCE_LABEL = "org.briarproject.bramble.contact/ALICE_NONCE";
/** /**
* Label for deriving Bob's key binding nonce from the master key. * Label for deriving Bob's key binding nonce from the master secret.
*/ */
String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE"; String BOB_NONCE_LABEL = "org.briarproject.bramble.contact/BOB_NONCE";
/** /**
* Exchanges contact information with a remote peer. * Exchanges contact information with a remote peer.
*/ */
void startExchange(LocalAuthor localAuthor, SecretKey masterKey, void startExchange(ContactExchangeListener listener,
LocalAuthor localAuthor, SecretKey masterSecret,
DuplexTransportConnection conn, TransportId transportId, DuplexTransportConnection conn, TransportId transportId,
boolean alice); boolean alice);
} }

View File

@@ -24,80 +24,61 @@ public interface ContactManager {
void registerContactHook(ContactHook hook); void registerContactHook(ContactHook hook);
/** /**
* Stores a contact with the given pseudonym, derives and stores transport * Stores a contact associated with the given local and remote pseudonyms,
* keys for each transport, and returns an ID for the contact. * derives and stores transport keys for each transport, and returns an ID
* for the contact.
* *
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
*/ */
ContactId addContact(Transaction txn, Author a, SecretKey rootKey, ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms
* and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID
* for the contact.
*
* @param alice true if the local party is Alice
*/
ContactId addContact(Author remote, AuthorId local, SecretKey master,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
throws DbException; throws DbException;
/**
* Stores a contact with the given pseudonym and returns an ID for the
* contact.
*/
ContactId addContact(Transaction txn, Author a, boolean verified)
throws DbException;
/**
* Stores a contact with the given pseudonym, derives and stores transport
* keys for each transport, and returns an ID for the contact.
*
* @param alice true if the local party is Alice
*/
ContactId addContact(Author a, SecretKey rootKey, long timestamp,
boolean alice, boolean verified, boolean active) throws DbException;
/**
* Returns the static link that needs to be sent to the contact to be added.
*/
String getRemoteContactLink();
/**
* Returns true if the given link is syntactically valid.
*/
boolean isValidRemoteContactLink(String link);
/**
* Requests a new contact to be added via the given {@code link}.
*
* @param link The link received from the contact we want to add.
* @param alias The alias the user has given this contact.
* @return A PendingContact representing the contact to be added.
*/
PendingContact addRemoteContactRequest(String link, String alias);
/**
* Returns a list of {@link PendingContact}s.
*/
Collection<PendingContact> getPendingContacts();
/**
* Removes a {@link PendingContact} that is in state
* {@link PendingContactState FAILED}.
*/
void removePendingContact(PendingContact pendingContact);
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.
*/ */
Contact getContact(ContactId c) throws DbException; Contact getContact(ContactId c) throws DbException;
/** /**
* Returns the contact with the given ID. * Returns the contact with the given remoteAuthorId
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws org.briarproject.bramble.api.db.NoSuchContactException
*/ */
Contact getContact(AuthorId a) throws DbException; Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException;
/** /**
* Returns the contact with the given ID. * Returns the contact with the given remoteAuthorId
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws org.briarproject.bramble.api.db.NoSuchContactException
*/ */
Contact getContact(Transaction txn, AuthorId a) throws DbException; Contact getContact(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException;
/** /**
* Returns all active contacts. * Returns all active contacts.
*/ */
Collection<Contact> getContacts() throws DbException; Collection<Contact> getActiveContacts() throws DbException;
/** /**
* Removes a contact and all associated state. * Removes a contact and all associated state.
@@ -109,6 +90,12 @@ public interface ContactManager {
*/ */
void removeContact(Transaction txn, ContactId c) throws DbException; void removeContact(Transaction txn, ContactId c) throws DbException;
/**
* Marks a contact as active or inactive.
*/
void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException;
/** /**
* Sets an alias name for the contact or unsets it if alias is null. * Sets an alias name for the contact or unsets it if alias is null.
*/ */
@@ -122,14 +109,16 @@ public interface ContactManager {
throws DbException; throws DbException;
/** /**
* Returns true if a contact with this pseudonym already exists. * Return true if a contact with this name and public key already exists
*/ */
boolean contactExists(Transaction txn, AuthorId a) throws DbException; boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException;
/** /**
* Returns true if a contact with this pseudonym already exists. * Return true if a contact with this name and public key already exists
*/ */
boolean contactExists(AuthorId a) throws DbException; boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException;
/** /**
* Returns the {@link AuthorInfo} for the given author. * Returns the {@link AuthorInfo} for the given author.

View File

@@ -1,56 +0,0 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class PendingContact {
private final PendingContactId id;
private final byte[] publicKey;
private final String alias;
private final PendingContactState state;
private final long timestamp;
public PendingContact(PendingContactId id, byte[] publicKey,
String alias, PendingContactState state, long timestamp) {
this.id = id;
this.publicKey = publicKey;
this.alias = alias;
this.state = state;
this.timestamp = timestamp;
}
public PendingContactId getId() {
return id;
}
public byte[] getPublicKey() {
return publicKey;
}
public String getAlias() {
return alias;
}
public PendingContactState getState() {
return state;
}
public long getTimestamp() {
return timestamp;
}
@Override
public int hashCode() {
return id.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof PendingContact &&
id.equals(((PendingContact) o).id);
}
}

View File

@@ -1,25 +0,0 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.ThreadSafe;
/**
* Type-safe wrapper for a byte array that uniquely identifies a
* {@link PendingContact}.
*/
@ThreadSafe
@NotNullByDefault
public class PendingContactId extends UniqueId {
public PendingContactId(byte[] id) {
super(id);
}
@Override
public boolean equals(Object o) {
return o instanceof PendingContactId && super.equals(o);
}
}

View File

@@ -1,30 +0,0 @@
package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public enum PendingContactState {
WAITING_FOR_CONNECTION(0),
CONNECTED(1),
ADDING_CONTACT(2),
FAILED(3);
private final int value;
PendingContactState(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public static PendingContactState fromValue(int value) {
for (PendingContactState s : values()) if (s.value == value) return s;
throw new IllegalArgumentException();
}
}

View File

@@ -14,12 +14,18 @@ import javax.annotation.concurrent.Immutable;
public class ContactAddedEvent extends Event { public class ContactAddedEvent extends Event {
private final ContactId contactId; private final ContactId contactId;
private final boolean active;
public ContactAddedEvent(ContactId contactId) { public ContactAddedEvent(ContactId contactId, boolean active) {
this.contactId = contactId; this.contactId = contactId;
this.active = active;
} }
public ContactId getContactId() { public ContactId getContactId() {
return contactId; return contactId;
} }
public boolean isActive() {
return active;
}
} }

View File

@@ -1,32 +0,0 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
@NotNullByDefault
public class ContactExchangeFailedEvent extends Event {
@Nullable
private final Author duplicateRemoteAuthor;
public ContactExchangeFailedEvent(@Nullable Author duplicateRemoteAuthor) {
this.duplicateRemoteAuthor = duplicateRemoteAuthor;
}
public ContactExchangeFailedEvent() {
this(null);
}
@Nullable
public Author getDuplicateRemoteAuthor() {
return duplicateRemoteAuthor;
}
public boolean wasDuplicateContact() {
return duplicateRemoteAuthor != null;
}
}

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public class ContactExchangeSucceededEvent extends Event {
private final Author remoteAuthor;
public ContactExchangeSucceededEvent(Author remoteAuthor) {
this.remoteAuthor = remoteAuthor;
}
public Author getRemoteAuthor() {
return remoteAuthor;
}
}

View File

@@ -0,0 +1,31 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a contact is marked active or inactive.
*/
@Immutable
@NotNullByDefault
public class ContactStatusChangedEvent extends Event {
private final ContactId contactId;
private final boolean active;
public ContactStatusChangedEvent(ContactId contactId, boolean active) {
this.contactId = contactId;
this.active = active;
}
public ContactId getContactId() {
return contactId;
}
public boolean isActive() {
return active;
}
}

View File

@@ -1,34 +0,0 @@
package org.briarproject.bramble.api.contact.event;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* An event that is broadcast when a pending contact's state is changed.
*/
@Immutable
@NotNullByDefault
public class PendingContactStateChangedEvent extends Event {
private final PendingContactId id;
private final PendingContactState state;
public PendingContactStateChangedEvent(PendingContactId id,
PendingContactState state) {
this.id = id;
this.state = state;
}
public PendingContactId getId() {
return id;
}
public PendingContactState getPendingContactState() {
return state;
}
}

View File

@@ -2,7 +2,7 @@ package org.briarproject.bramble.api.crypto;
/** /**
* Crypto operations for the key agreement protocol - see * Crypto operations for the key agreement protocol - see
* https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BQP.md * https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md
*/ */
public interface KeyAgreementCrypto { public interface KeyAgreementCrypto {

View File

@@ -1,45 +1,29 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.api.crypto;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
/** /**
* Crypto operations for the transport security protocol - see * Crypto operations for the transport security protocol - see
* https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BTP.md * https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BTP.md
*/ */
public interface TransportCrypto { public interface TransportCrypto {
/** /**
* Derives initial transport keys for the given transport in the given * Derives initial transport keys for the given transport in the given
* time period from the given root key. * rotation period from the given master secret.
* *
* @param alice whether the keys are for use by Alice or Bob. * @param alice whether the keys are for use by Alice or Bob.
* @param active whether the keys are usable for outgoing streams. * @param active whether the keys are usable for outgoing streams.
*/ */
TransportKeys deriveTransportKeys(TransportId t, SecretKey rootKey, TransportKeys deriveTransportKeys(TransportId t, SecretKey master,
long timePeriod, boolean alice, boolean active); long rotationPeriod, boolean alice, boolean active);
/** /**
* Rotates the given transport keys to the given time period. If the keys * Rotates the given transport keys to the given rotation period. If the
* are for the given period or any later period they are not rotated. * keys are for the given period or any later period they are not rotated.
*/ */
TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod); TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/**
* Derives handshake keys for the given transport in the given time period
* from the given root key.
*
* @param alice whether the keys are for use by Alice or Bob.
*/
HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean alice);
/**
* Updates the given handshake keys to the given time period. If the keys
* are for the given period or any later period they are not updated.
*/
HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod);
/** /**
* Encodes the pseudo-random tag that is used to recognise a stream. * Encodes the pseudo-random tag that is used to recognise a stream.

View File

@@ -1,20 +0,0 @@
package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.event.EventExecutor;
/**
* An action that's taken when a {@link Transaction} is committed.
*/
public interface CommitAction {
void accept(Visitor visitor);
interface Visitor {
@EventExecutor
void visit(EventAction a);
@EventExecutor
void visit(TaskAction a);
}
}

View File

@@ -2,8 +2,6 @@ package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -22,11 +20,8 @@ import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer; import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request; import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.HandshakeKeySet; import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -102,31 +97,17 @@ public interface DatabaseComponent {
NullableDbCallable<R, E> task) throws DbException, E; NullableDbCallable<R, E> task) throws DbException, E;
/** /**
* Stores a contact with the given pseudonym and returns an ID for the * Stores a contact associated with the given local and remote pseudonyms,
* contact. * and returns an ID for the contact.
*/ */
ContactId addContact(Transaction txn, Author a, boolean verified) ContactId addContact(Transaction txn, Author remote, AuthorId local,
throws DbException; boolean verified, boolean active) throws DbException;
/** /**
* Stores a group. * Stores a group.
*/ */
void addGroup(Transaction txn, Group g) throws DbException; void addGroup(Transaction txn, Group g) throws DbException;
/**
* Stores the given handshake keys for the given contact and returns a
* key set ID.
*/
HandshakeKeySetId addHandshakeKeys(Transaction txn, ContactId c,
HandshakeKeys k) throws DbException;
/**
* Stores the given handshake keys for the given pending contact and
* returns a key set ID.
*/
HandshakeKeySetId addHandshakeKeys(Transaction txn, PendingContactId p,
HandshakeKeys k) throws DbException;
/** /**
* Stores a local pseudonym. * Stores a local pseudonym.
*/ */
@@ -138,12 +119,6 @@ public interface DatabaseComponent {
void addLocalMessage(Transaction txn, Message m, Metadata meta, void addLocalMessage(Transaction txn, Message m, Metadata meta,
boolean shared) throws DbException; boolean shared) throws DbException;
/**
* Stores a pending contact.
*/
void addPendingContact(Transaction txn, PendingContact p)
throws DbException;
/** /**
* Stores a transport. * Stores a transport.
*/ */
@@ -154,39 +129,27 @@ public interface DatabaseComponent {
* Stores the given transport keys for the given contact and returns a * Stores the given transport keys for the given contact and returns a
* key set ID. * key set ID.
*/ */
TransportKeySetId addTransportKeys(Transaction txn, ContactId c, KeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException; TransportKeys k) throws DbException;
/** /**
* Returns true if the database contains the given contact. * Returns true if the database contains the given contact for the given
* <p/> * local pseudonym.
* Read-only.
*/ */
boolean containsContact(Transaction txn, AuthorId a) throws DbException; boolean containsContact(Transaction txn, AuthorId remote, AuthorId local)
throws DbException;
/** /**
* Returns true if the database contains the given group. * Returns true if the database contains the given group.
* <p/>
* Read-only.
*/ */
boolean containsGroup(Transaction txn, GroupId g) throws DbException; boolean containsGroup(Transaction txn, GroupId g) throws DbException;
/** /**
* Returns true if the database contains the given local author. * Returns true if the database contains the given local author.
* <p/>
* Read-only.
*/ */
boolean containsLocalAuthor(Transaction txn, AuthorId local) boolean containsLocalAuthor(Transaction txn, AuthorId local)
throws DbException; throws DbException;
/**
* Returns true if the database contains the given pending contact.
* <p/>
* Read-only.
*/
boolean containsPendingContact(Transaction txn, PendingContactId p)
throws DbException;
/** /**
* Deletes the message with the given ID. Unlike * Deletes the message with the given ID. Unlike
* {@link #removeMessage(Transaction, MessageId)}, the message ID, * {@link #removeMessage(Transaction, MessageId)}, the message ID,
@@ -245,6 +208,24 @@ public interface DatabaseComponent {
Collection<Message> generateRequestedBatch(Transaction txn, ContactId c, Collection<Message> generateRequestedBatch(Transaction txn, ContactId c,
int maxLength, int maxLatency) throws DbException; int maxLength, int maxLatency) throws DbException;
/**
* Returns the number of blocks in the given message.
* <p>
* Read-only.
*/
int getBlockCount(Transaction txn, MessageId m) throws DbException;
/**
* Returns the given block of the given message.
* <p>
* Read-only.
*
* @throws NoSuchBlockException if 'blockNumber' is greater than or equal
* to the number of blocks in the message
*/
byte[] getBlock(Transaction txn, MessageId m, int blockNumber)
throws DbException;
/** /**
* Returns the contact with the given ID. * Returns the contact with the given ID.
* <p/> * <p/>
@@ -252,13 +233,6 @@ public interface DatabaseComponent {
*/ */
Contact getContact(Transaction txn, ContactId c) throws DbException; Contact getContact(Transaction txn, ContactId c) throws DbException;
/**
* Returns the contact with the given author ID.
* <p/>
* Read-only.
*/
Contact getContact(Transaction txn, AuthorId a) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
* <p/> * <p/>
@@ -266,6 +240,22 @@ public interface DatabaseComponent {
*/ */
Collection<Contact> getContacts(Transaction txn) throws DbException; Collection<Contact> getContacts(Transaction txn) throws DbException;
/**
* Returns a possibly empty collection of contacts with the given author ID.
* <p/>
* Read-only.
*/
Collection<Contact> getContactsByAuthorId(Transaction txn, AuthorId remote)
throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(Transaction txn, AuthorId a)
throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/> * <p/>
@@ -297,14 +287,6 @@ public interface DatabaseComponent {
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g) Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
throws DbException; throws DbException;
/**
* Returns all handshake keys for the given transport.
* <p/>
* Read-only.
*/
Collection<HandshakeKeySet> getHandshakeKeys(Transaction txn, TransportId t)
throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
@@ -453,14 +435,6 @@ public interface DatabaseComponent {
*/ */
long getNextSendTime(Transaction txn, ContactId c) throws DbException; long getNextSendTime(Transaction txn, ContactId c) throws DbException;
/**
* Returns all pending contacts.
* <p/>
* Read-only.
*/
Collection<PendingContact> getPendingContacts(Transaction txn)
throws DbException;
/** /**
* Returns all settings in the given namespace. * Returns all settings in the given namespace.
* <p/> * <p/>
@@ -473,20 +447,14 @@ public interface DatabaseComponent {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t) Collection<KeySet> getTransportKeys(Transaction txn, TransportId t)
throws DbException; throws DbException;
/**
* Increments the outgoing stream counter for the given handshake keys.
*/
void incrementStreamCounter(Transaction txn, TransportId t,
HandshakeKeySetId k) throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */
void incrementStreamCounter(Transaction txn, TransportId t, void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k)
TransportKeySetId k) throws DbException; throws DbException;
/** /**
* Merges the given metadata with the existing metadata for the given * Merges the given metadata with the existing metadata for the given
@@ -541,12 +509,6 @@ public interface DatabaseComponent {
*/ */
void removeGroup(Transaction txn, Group g) throws DbException; void removeGroup(Transaction txn, Group g) throws DbException;
/**
* Removes the given handshake keys from the database.
*/
void removeHandshakeKeys(Transaction txn, TransportId t,
HandshakeKeySetId k) throws DbException;
/** /**
* Removes a local pseudonym (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
@@ -557,12 +519,6 @@ public interface DatabaseComponent {
*/ */
void removeMessage(Transaction txn, MessageId m) throws DbException; void removeMessage(Transaction txn, MessageId m) throws DbException;
/**
* Removes a pending contact (and all associated state) from the database.
*/
void removePendingContact(Transaction txn, PendingContactId p)
throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -571,14 +527,20 @@ public interface DatabaseComponent {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(Transaction txn, TransportId t, void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
TransportKeySetId k) throws DbException; throws DbException;
/** /**
* Marks the given contact as verified. * Marks the given contact as verified.
*/ */
void setContactVerified(Transaction txn, ContactId c) throws DbException; void setContactVerified(Transaction txn, ContactId c) throws DbException;
/**
* Marks the given contact as active or inactive.
*/
void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException;
/** /**
* Sets an alias name for the contact or unsets it if alias is null. * Sets an alias name for the contact or unsets it if alias is null.
*/ */
@@ -609,36 +571,21 @@ public interface DatabaseComponent {
Collection<MessageId> dependencies) throws DbException; Collection<MessageId> dependencies) throws DbException;
/** /**
* Sets the reordering window for the given transport key set in the given * Sets the reordering window for the given key set and transport in the
* time period. * given rotation period.
*/ */
void setReorderingWindow(Transaction txn, TransportKeySetId k, void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
TransportId t, long timePeriod, long base, byte[] bitmap) long rotationPeriod, long base, byte[] bitmap) throws DbException;
throws DbException;
/**
* Sets the reordering window for the given handshake key set in the given
* time period.
*/
void setReorderingWindow(Transaction txn, HandshakeKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void setTransportKeysActive(Transaction txn, TransportId t, void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
TransportKeySetId k) throws DbException;
/**
* Stores the given handshake keys, deleting any keys they have replaced.
*/
void updateHandshakeKeys(Transaction txn, Collection<HandshakeKeySet> keys)
throws DbException; throws DbException;
/** /**
* Stores the given transport keys, deleting any keys they have replaced. * Stores the given transport keys, deleting any keys they have replaced.
*/ */
void updateTransportKeys(Transaction txn, Collection<TransportKeySet> keys) void updateTransportKeys(Transaction txn, Collection<KeySet> keys)
throws DbException; throws DbException;
} }

View File

@@ -10,4 +10,6 @@ public interface DatabaseConfig {
File getDatabaseDirectory(); File getDatabaseDirectory();
File getDatabaseKeyDirectory(); File getDatabaseKeyDirectory();
long getMaxSize();
} }

View File

@@ -1,24 +0,0 @@
package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.event.Event;
/**
* A {@link CommitAction} that broadcasts an event.
*/
public class EventAction implements CommitAction {
private final Event event;
EventAction(Event event) {
this.event = event;
}
public Event getEvent() {
return event;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when a database operation is attempted for a block that is not in
* the database. This exception may occur due to concurrent updates and does
* not indicate a database error.
*/
public class NoSuchBlockException extends DbException {
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when a database operation is attempted for a pending contact that is
* not in the database. This exception may occur due to concurrent updates and
* does not indicate a database error.
*/
public class NoSuchPendingContactException extends DbException {
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.db;
/**
* Thrown when a duplicate pending contact is added to the database. This
* exception may occur due to concurrent updates and does not indicate a
* database error.
*/
public class PendingContactExistsException extends DbException {
}

View File

@@ -1,24 +0,0 @@
package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.event.EventExecutor;
/**
* A {@link CommitAction} that submits a task to the {@link EventExecutor}.
*/
public class TaskAction implements CommitAction {
private final Runnable task;
TaskAction(Runnable task) {
this.task = task;
}
public Runnable getTask() {
return task;
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}

View File

@@ -1,15 +1,13 @@
package org.briarproject.bramble.api.db; package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventExecutor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
import static java.util.Collections.emptyList;
/** /**
* A wrapper around a database transaction. Transactions are not thread-safe. * A wrapper around a database transaction. Transactions are not thread-safe.
*/ */
@@ -19,7 +17,7 @@ public class Transaction {
private final Object txn; private final Object txn;
private final boolean readOnly; private final boolean readOnly;
private List<CommitAction> actions = null; private List<Event> events = null;
private boolean committed = false; private boolean committed = false;
public Transaction(Object txn, boolean readOnly) { public Transaction(Object txn, boolean readOnly) {
@@ -44,27 +42,19 @@ public class Transaction {
/** /**
* Attaches an event to be broadcast when the transaction has been * Attaches an event to be broadcast when the transaction has been
* committed. The event will be broadcast on the {@link EventExecutor}. * committed.
*/ */
public void attach(Event e) { public void attach(Event e) {
if (actions == null) actions = new ArrayList<>(); if (events == null) events = new ArrayList<>();
actions.add(new EventAction(e)); events.add(e);
} }
/** /**
* Attaches a task to be executed when the transaction has been * Returns any events attached to the transaction.
* committed. The task will be run on the {@link EventExecutor}.
*/ */
public void attach(Runnable r) { public List<Event> getEvents() {
if (actions == null) actions = new ArrayList<>(); if (events == null) return Collections.emptyList();
actions.add(new TaskAction(r)); return events;
}
/**
* Returns any actions attached to the transaction.
*/
public List<CommitAction> getActions() {
return actions == null ? emptyList() : actions;
} }
/** /**

View File

@@ -16,8 +16,7 @@ public interface EventBus {
void removeListener(EventListener l); void removeListener(EventListener l);
/** /**
* Asynchronously notifies all listeners of an event. Listeners are * Notifies all listeners of an event.
* notified on the {@link EventExecutor}.
*/ */
void broadcast(Event e); void broadcast(Event e);
} }

View File

@@ -1,26 +0,0 @@
package org.briarproject.bramble.api.event;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.inject.Qualifier;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
/**
* Annotation for injecting the executor for broadcasting events and running
* tasks that need to run in a defined order with respect to events. Also used
* for annotating methods that should run on the event executor.
* <p>
* The contract of this executor is that tasks are run in the order they're
* submitted, tasks are not run concurrently, and submitting a task will never
* block. Tasks must not block. Tasks submitted during shutdown are discarded.
*/
@Qualifier
@Target({FIELD, METHOD, PARAMETER})
@Retention(RUNTIME)
public @interface EventExecutor {
}

View File

@@ -12,6 +12,5 @@ public interface EventListener {
* Called when an event is broadcast. Implementations of this method must * Called when an event is broadcast. Implementations of this method must
* not block. * not block.
*/ */
@EventExecutor
void eventOccurred(Event e); void eventOccurred(Event e);
} }

View File

@@ -2,11 +2,8 @@ package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
/** /**
* A pseudonym for the local user. * A pseudonym for the local user.
*/ */
@@ -15,8 +12,6 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_K
public class LocalAuthor extends Author { public class LocalAuthor extends Author {
private final byte[] privateKey; private final byte[] privateKey;
@Nullable
private final byte[] handshakePublicKey, handshakePrivateKey;
private final long created; private final long created;
public LocalAuthor(AuthorId id, int formatVersion, String name, public LocalAuthor(AuthorId id, int formatVersion, String name,
@@ -24,22 +19,6 @@ public class LocalAuthor extends Author {
super(id, formatVersion, name, publicKey); super(id, formatVersion, name, publicKey);
this.privateKey = privateKey; this.privateKey = privateKey;
this.created = created; this.created = created;
handshakePublicKey = null;
handshakePrivateKey = null;
}
public LocalAuthor(AuthorId id, int formatVersion, String name,
byte[] publicKey, byte[] privateKey, byte[] handshakePublicKey,
byte[] handshakePrivateKey, long created) {
super(id, formatVersion, name, publicKey);
if (handshakePublicKey.length == 0 ||
handshakePublicKey.length > MAX_PUBLIC_KEY_LENGTH) {
throw new IllegalArgumentException();
}
this.privateKey = privateKey;
this.handshakePublicKey = handshakePublicKey;
this.handshakePrivateKey = handshakePrivateKey;
this.created = created;
} }
/** /**
@@ -49,22 +28,6 @@ public class LocalAuthor extends Author {
return privateKey; return privateKey;
} }
/**
* Returns the public key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePublicKey() {
return handshakePublicKey;
}
/**
* Returns the private key used for handshaking, or null if no key exists.
*/
@Nullable
public byte[] getHandshakePrivateKey() {
return handshakePrivateKey;
}
/** /**
* Returns the time the pseudonym was created, in milliseconds since the * Returns the time the pseudonym was created, in milliseconds since the
* Unix epoch. * Unix epoch.

View File

@@ -0,0 +1,21 @@
package org.briarproject.bramble.api.io;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchBlockException;
import org.briarproject.bramble.api.sync.MessageId;
public interface BlockSource {
/**
* Returns the number of blocks in the given message.
*/
int getBlockCount(MessageId m) throws DbException;
/**
* Returns the given block of the given message.
*
* @throws NoSuchBlockException if 'blockNumber' is greater than or equal
* to the number of blocks in the message
*/
byte[] getBlock(MessageId m, int blockNumber) throws DbException;
}

View File

@@ -0,0 +1,17 @@
package org.briarproject.bramble.api.io;
import org.briarproject.bramble.api.sync.MessageId;
import java.io.IOException;
import java.io.InputStream;
public interface MessageInputStreamFactory {
/**
* Returns an {@link InputStream} for reading the given message from the
* database. This method returns immediately. If the message is not in the
* database or cannot be read, reading from the stream will throw an
* {@link IOException};
*/
InputStream getMessageInputStream(MessageId m);
}

View File

@@ -40,8 +40,8 @@ public interface KeyAgreementConstants {
"org.briarproject.bramble.keyagreement/SHARED_SECRET"; "org.briarproject.bramble.keyagreement/SHARED_SECRET";
/** /**
* Label for deriving the master key. * Label for deriving the master secret.
*/ */
String MASTER_KEY_LABEL = String MASTER_SECRET_LABEL =
"org.briarproject.bramble.keyagreement/MASTER_SECRET"; "org.briarproject.bramble.keyagreement/MASTER_SECRET";
} }

View File

@@ -16,7 +16,6 @@ public interface TorConstants {
String PREF_TOR_NETWORK = "network2"; String PREF_TOR_NETWORK = "network2";
String PREF_TOR_PORT = "port"; String PREF_TOR_PORT = "port";
String PREF_TOR_MOBILE = "useMobileData"; String PREF_TOR_MOBILE = "useMobileData";
String PREF_TOR_ONLY_WHEN_CHARGING = "onlyWhenCharging";
int PREF_TOR_NETWORK_AUTOMATIC = 0; int PREF_TOR_NETWORK_AUTOMATIC = 0;
int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1; int PREF_TOR_NETWORK_WITHOUT_BRIDGES = 1;

View File

@@ -35,4 +35,9 @@ public interface SyncConstants {
* The maximum number of message IDs in an ack, offer or request record. * The maximum number of message IDs in an ack, offer or request record.
*/ */
int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH; int MAX_MESSAGE_IDS = MAX_RECORD_PAYLOAD_BYTES / UniqueId.LENGTH;
/**
* The maximum length of a message block in bytes.
*/
int MAX_BLOCK_LENGTH = 32 * 2014; // 32 KiB
} }

View File

@@ -1,57 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* Abstract superclass for {@link TransportKeys} and {@link HandshakeKeys}.
*/
@Immutable
@NotNullByDefault
public abstract class AbstractTransportKeys {
private final TransportId transportId;
private final IncomingKeys inPrev, inCurr, inNext;
private final OutgoingKeys outCurr;
AbstractTransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
if (inPrev.getTimePeriod() != outCurr.getTimePeriod() - 1)
throw new IllegalArgumentException();
if (inCurr.getTimePeriod() != outCurr.getTimePeriod())
throw new IllegalArgumentException();
if (inNext.getTimePeriod() != outCurr.getTimePeriod() + 1)
throw new IllegalArgumentException();
this.transportId = transportId;
this.inPrev = inPrev;
this.inCurr = inCurr;
this.inNext = inNext;
this.outCurr = outCurr;
}
public TransportId getTransportId() {
return transportId;
}
public IncomingKeys getPreviousIncomingKeys() {
return inPrev;
}
public IncomingKeys getCurrentIncomingKeys() {
return inCurr;
}
public IncomingKeys getNextIncomingKeys() {
return inNext;
}
public OutgoingKeys getCurrentOutgoingKeys() {
return outCurr;
}
public long getTimePeriod() {
return outCurr.getTimePeriod();
}
}

View File

@@ -1,70 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
/**
* A set of keys for handshaking with a given contact or pending contact over a
* given transport. Unlike a {@link TransportKeySet} these keys do not provide
* forward secrecy.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeySet {
private final HandshakeKeySetId keySetId;
@Nullable
private final ContactId contactId;
@Nullable
private final PendingContactId pendingContactId;
private final HandshakeKeys keys;
public HandshakeKeySet(HandshakeKeySetId keySetId, ContactId contactId,
HandshakeKeys keys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.keys = keys;
pendingContactId = null;
}
public HandshakeKeySet(HandshakeKeySetId keySetId,
PendingContactId pendingContactId, HandshakeKeys keys) {
this.keySetId = keySetId;
this.pendingContactId = pendingContactId;
this.keys = keys;
contactId = null;
}
public HandshakeKeySetId getKeySetId() {
return keySetId;
}
@Nullable
public ContactId getContactId() {
return contactId;
}
@Nullable
public PendingContactId getPendingContactId() {
return pendingContactId;
}
public HandshakeKeys getKeys() {
return keys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof HandshakeKeySet &&
keySetId.equals(((HandshakeKeySet) o).keySetId);
}
}

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* Type-safe wrapper for an integer that uniquely identifies a
* {@link HandshakeKeySet set of handshake keys} within the scope of the local
* device.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeySetId {
private final int id;
public HandshakeKeySetId(int id) {
this.id = id;
}
public int getInt() {
return id;
}
@Override
public int hashCode() {
return id;
}
@Override
public boolean equals(Object o) {
return o instanceof HandshakeKeySetId &&
id == ((HandshakeKeySetId) o).id;
}
}

View File

@@ -1,36 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/**
* Keys for handshaking with a given contact or pending contact over a given
* transport. Unlike {@link TransportKeys} these keys do not provide forward
* secrecy.
*/
@Immutable
@NotNullByDefault
public class HandshakeKeys extends AbstractTransportKeys {
private final SecretKey rootKey;
private final boolean alice;
public HandshakeKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr,
SecretKey rootKey, boolean alice) {
super(transportId, inPrev, inCurr, inNext, outCurr);
this.rootKey = rootKey;
this.alice = alice;
}
public SecretKey getRootKey() {
return rootKey;
}
public boolean isAlice() {
return alice;
}
}

View File

@@ -1,35 +1,30 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
/** /**
* Contains transport keys for receiving streams from a given contact over a * Contains transport keys for receiving streams from a given contact over a
* given transport in a given time period. * given transport in a given rotation period.
*/ */
@Immutable
@NotNullByDefault
public class IncomingKeys { public class IncomingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long timePeriod, windowBase; private final long rotationPeriod, windowBase;
private final byte[] windowBitmap; private final byte[] windowBitmap;
public IncomingKeys(SecretKey tagKey, SecretKey headerKey, public IncomingKeys(SecretKey tagKey, SecretKey headerKey,
long timePeriod) { long rotationPeriod) {
this(tagKey, headerKey, timePeriod, 0, this(tagKey, headerKey, rotationPeriod, 0,
new byte[REORDERING_WINDOW_SIZE / 8]); new byte[REORDERING_WINDOW_SIZE / 8]);
} }
public IncomingKeys(SecretKey tagKey, SecretKey headerKey, public IncomingKeys(SecretKey tagKey, SecretKey headerKey,
long timePeriod, long windowBase, byte[] windowBitmap) { long rotationPeriod, long windowBase, byte[] windowBitmap) {
this.tagKey = tagKey; this.tagKey = tagKey;
this.headerKey = headerKey; this.headerKey = headerKey;
this.timePeriod = timePeriod; this.rotationPeriod = rotationPeriod;
this.windowBase = windowBase; this.windowBase = windowBase;
this.windowBitmap = windowBitmap; this.windowBitmap = windowBitmap;
} }
@@ -42,8 +37,8 @@ public class IncomingKeys {
return headerKey; return headerKey;
} }
public long getTimePeriod() { public long getRotationPeriod() {
return timePeriod; return rotationPeriod;
} }
public long getWindowBase() { public long getWindowBase() {

View File

@@ -27,14 +27,14 @@ public interface KeyManager {
* @param alice true if the local party is Alice * @param alice true if the local party is Alice
* @param active whether the derived keys can be used for outgoing streams * @param active whether the derived keys can be used for outgoing streams
*/ */
Map<TransportId, TransportKeySetId> addContact(Transaction txn, ContactId c, Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active) SecretKey master, long timestamp, boolean alice, boolean active)
throws DbException; throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void activateKeys(Transaction txn, Map<TransportId, TransportKeySetId> keys) void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
throws DbException; throws DbException;
/** /**

View File

@@ -0,0 +1,47 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A set of transport keys for communicating with a contact.
*/
@Immutable
@NotNullByDefault
public class KeySet {
private final KeySetId keySetId;
private final ContactId contactId;
private final TransportKeys transportKeys;
public KeySet(KeySetId keySetId, ContactId contactId,
TransportKeys transportKeys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.transportKeys = transportKeys;
}
public KeySetId getKeySetId() {
return keySetId;
}
public ContactId getContactId() {
return contactId;
}
public TransportKeys getTransportKeys() {
return transportKeys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof KeySet && keySetId.equals(((KeySet) o).keySetId);
}
}

View File

@@ -5,19 +5,18 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable; import javax.annotation.concurrent.Immutable;
/** /**
* Type-safe wrapper for an integer that uniquely identifies a * Type-safe wrapper for an integer that uniquely identifies a set of transport
* {@link TransportKeySet set of transport keys} within the scope of the local * keys within the scope of the local device.
* device.
* <p/> * <p/>
* Key sets created on a given device must have increasing identifiers. * Key sets created on a given device must have increasing identifiers.
*/ */
@Immutable @Immutable
@NotNullByDefault @NotNullByDefault
public class TransportKeySetId { public class KeySetId {
private final int id; private final int id;
public TransportKeySetId(int id) { public KeySetId(int id) {
this.id = id; this.id = id;
} }
@@ -32,7 +31,6 @@ public class TransportKeySetId {
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
return o instanceof TransportKeySetId && return o instanceof KeySetId && id == ((KeySetId) o).id;
id == ((TransportKeySetId) o).id;
} }
} }

View File

@@ -1,32 +1,27 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/** /**
* Contains transport keys for sending streams to a given contact over a given * Contains transport keys for sending streams to a given contact over a given
* transport in a given time period. * transport in a given rotation period.
*/ */
@Immutable
@NotNullByDefault
public class OutgoingKeys { public class OutgoingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long timePeriod, streamCounter; private final long rotationPeriod, streamCounter;
private final boolean active; private final boolean active;
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey, public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long timePeriod, boolean active) { long rotationPeriod, boolean active) {
this(tagKey, headerKey, timePeriod, 0, active); this(tagKey, headerKey, rotationPeriod, 0, active);
} }
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey, public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long timePeriod, long streamCounter, boolean active) { long rotationPeriod, long streamCounter, boolean active) {
this.tagKey = tagKey; this.tagKey = tagKey;
this.headerKey = headerKey; this.headerKey = headerKey;
this.timePeriod = timePeriod; this.rotationPeriod = rotationPeriod;
this.streamCounter = streamCounter; this.streamCounter = streamCounter;
this.active = active; this.active = active;
} }
@@ -39,8 +34,8 @@ public class OutgoingKeys {
return headerKey; return headerKey;
} }
public long getTimePeriod() { public long getRotationPeriod() {
return timePeriod; return rotationPeriod;
} }
public long getStreamCounter() { public long getStreamCounter() {

View File

@@ -2,13 +2,8 @@ package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
@Immutable
@NotNullByDefault
public class StreamContext { public class StreamContext {
private final ContactId contactId; private final ContactId contactId;

View File

@@ -82,58 +82,30 @@ public interface TransportConstants {
int REORDERING_WINDOW_SIZE = 32; int REORDERING_WINDOW_SIZE = 32;
/** /**
* Label for deriving Alice's initial tag key from the root key in * Label for deriving Alice's initial tag key from the master secret.
* rotation mode.
*/ */
String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY"; String ALICE_TAG_LABEL = "org.briarproject.bramble.transport/ALICE_TAG_KEY";
/** /**
* Label for deriving Bob's initial tag key from the root key in rotation * Label for deriving Bob's initial tag key from the master secret.
* mode.
*/ */
String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY"; String BOB_TAG_LABEL = "org.briarproject.bramble.transport/BOB_TAG_KEY";
/** /**
* Label for deriving Alice's initial header key from the root key in * Label for deriving Alice's initial header key from the master secret.
* rotation mode.
*/ */
String ALICE_HEADER_LABEL = String ALICE_HEADER_LABEL =
"org.briarproject.bramble.transport/ALICE_HEADER_KEY"; "org.briarproject.bramble.transport/ALICE_HEADER_KEY";
/** /**
* Label for deriving Bob's initial header key from the root key in * Label for deriving Bob's initial header key from the master secret.
* rotation mode.
*/ */
String BOB_HEADER_LABEL = String BOB_HEADER_LABEL =
"org.briarproject.bramble.transport/BOB_HEADER_KEY"; "org.briarproject.bramble.transport/BOB_HEADER_KEY";
/** /**
* Label for deriving the next period's key in rotation mode. * Label for deriving the next period's key in key rotation.
*/ */
String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE"; String ROTATE_LABEL = "org.briarproject.bramble.transport/ROTATE";
/**
* Label for deriving Alice's tag key from the root key in handshake mode.
*/
String ALICE_HANDSHAKE_TAG_LABEL =
"org.briarproject.bramble.transport/ALICE_HANDSHAKE_TAG_KEY";
/**
* Label for deriving Bob's tag key from the root key in handshake mode.
*/
String BOB_HANDSHAKE_TAG_LABEL =
"org.briarproject.bramble.transport/BOB_HANDSHAKE_TAG_KEY";
/**
* Label for deriving Alice's header key from the root key in handshake
* mode.
*/
String ALICE_HANDSHAKE_HEADER_LABEL =
"org.briarproject.bramble.transport/ALICE_HANDSHAKE_HEADER_KEY";
/**
* Label for deriving Bob's header key from the root key in handshake mode.
*/
String BOB_HANDSHAKE_HEADER_LABEL =
"org.briarproject.bramble.transport/BOB_HANDSHAKE_HEADER_KEY";
} }

View File

@@ -1,49 +0,0 @@
package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.concurrent.Immutable;
/**
* A set of keys for communicating with a given contact over a given transport.
* Unlike a {@link HandshakeKeySet} these keys provide forward secrecy.
*/
@Immutable
@NotNullByDefault
public class TransportKeySet {
private final TransportKeySetId keySetId;
private final ContactId contactId;
private final TransportKeys keys;
public TransportKeySet(TransportKeySetId keySetId, ContactId contactId,
TransportKeys keys) {
this.keySetId = keySetId;
this.contactId = contactId;
this.keys = keys;
}
public TransportKeySetId getKeySetId() {
return keySetId;
}
public ContactId getContactId() {
return contactId;
}
public TransportKeys getKeys() {
return keys;
}
@Override
public int hashCode() {
return keySetId.hashCode();
}
@Override
public boolean equals(Object o) {
return o instanceof TransportKeySet &&
keySetId.equals(((TransportKeySet) o).keySetId);
}
}

View File

@@ -1,20 +1,52 @@
package org.briarproject.bramble.api.transport; package org.briarproject.bramble.api.transport;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import javax.annotation.concurrent.Immutable;
/** /**
* Keys for communicating with a given contact over a given transport. Unlike * Keys for communicating with a given contact over a given transport.
* {@link HandshakeKeys} these keys provide forward secrecy.
*/ */
@Immutable public class TransportKeys {
@NotNullByDefault
public class TransportKeys extends AbstractTransportKeys { private final TransportId transportId;
private final IncomingKeys inPrev, inCurr, inNext;
private final OutgoingKeys outCurr;
public TransportKeys(TransportId transportId, IncomingKeys inPrev, public TransportKeys(TransportId transportId, IncomingKeys inPrev,
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) { IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
super(transportId, inPrev, inCurr, inNext, outCurr); if (inPrev.getRotationPeriod() != inCurr.getRotationPeriod() - 1)
throw new IllegalArgumentException();
if (inNext.getRotationPeriod() != inCurr.getRotationPeriod() + 1)
throw new IllegalArgumentException();
if (outCurr.getRotationPeriod() != inCurr.getRotationPeriod())
throw new IllegalArgumentException();
this.transportId = transportId;
this.inPrev = inPrev;
this.inCurr = inCurr;
this.inNext = inNext;
this.outCurr = outCurr;
}
public TransportId getTransportId() {
return transportId;
}
public IncomingKeys getPreviousIncomingKeys() {
return inPrev;
}
public IncomingKeys getCurrentIncomingKeys() {
return inCurr;
}
public IncomingKeys getNextIncomingKeys() {
return inNext;
}
public OutgoingKeys getCurrentOutgoingKeys() {
return outCurr;
}
public long getRotationPeriod() {
return outCurr.getRotationPeriod();
} }
} }

View File

@@ -38,13 +38,6 @@ public interface ClientVersioningManager {
Visibility getClientVisibility(Transaction txn, ContactId contactId, Visibility getClientVisibility(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException; ClientId clientId, int majorVersion) throws DbException;
/**
* Returns the minor version of the given client that is supported by the
* given contact, or -1 if the contact does not support the client.
*/
int getClientMinorVersion(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException;
interface ClientVersioningHook { interface ClientVersioningHook {
void onClientVisibilityChanging(Transaction txn, Contact c, void onClientVisibilityChanging(Transaction txn, Contact c,

View File

@@ -1,10 +1,6 @@
package org.briarproject.bramble.test; package org.briarproject.bramble.test;
import org.briarproject.bramble.api.UniqueId; import org.briarproject.bramble.api.UniqueId;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -29,7 +25,6 @@ import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION; import static org.briarproject.bramble.api.identity.Author.FORMAT_VERSION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
@@ -46,7 +41,6 @@ public class TestUtils {
new AtomicInteger((int) (Math.random() * 1000 * 1000)); new AtomicInteger((int) (Math.random() * 1000 * 1000));
private static final Random random = new Random(); private static final Random random = new Random();
private static final long timestamp = System.currentTimeMillis(); private static final long timestamp = System.currentTimeMillis();
private static final AtomicInteger nextContactId = new AtomicInteger(1);
public static File getTestDirectory() { public static File getTestDirectory() {
int name = nextTestDir.getAndIncrement(); int name = nextTestDir.getAndIncrement();
@@ -146,35 +140,6 @@ public class TestUtils {
return new Message(id, groupId, timestamp, body); return new Message(id, groupId, timestamp, body);
} }
public static PendingContact getPendingContact() {
return getPendingContact(1 + random.nextInt(MAX_AUTHOR_NAME_LENGTH));
}
public static PendingContact getPendingContact(int nameLength) {
PendingContactId id = new PendingContactId(getRandomId());
byte[] publicKey = getRandomBytes(MAX_PUBLIC_KEY_LENGTH);
String alias = getRandomString(nameLength);
return new PendingContact(id, publicKey, alias, WAITING_FOR_CONNECTION,
timestamp);
}
public static ContactId getContactId() {
return new ContactId(nextContactId.getAndIncrement());
}
public static Contact getContact() {
return getContact(getAuthor(), random.nextBoolean());
}
public static Contact getContact(Author a, boolean verified) {
return getContact(getContactId(), a, verified);
}
public static Contact getContact(ContactId c, Author a, boolean verified) {
return new Contact(c, a, getRandomString(MAX_AUTHOR_NAME_LENGTH),
getRandomBytes(MAX_PUBLIC_KEY_LENGTH), verified);
}
public static double getMedian(Collection<? extends Number> samples) { public static double getMedian(Collection<? extends Number> samples) {
int size = samples.size(); int size = samples.size();
if (size == 0) throw new IllegalArgumentException(); if (size == 0) throw new IllegalArgumentException();

View File

@@ -9,6 +9,7 @@ import org.briarproject.bramble.db.DatabaseExecutorModule;
import org.briarproject.bramble.db.DatabaseModule; import org.briarproject.bramble.db.DatabaseModule;
import org.briarproject.bramble.event.EventModule; import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.identity.IdentityModule; import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.io.IoModule;
import org.briarproject.bramble.keyagreement.KeyAgreementModule; import org.briarproject.bramble.keyagreement.KeyAgreementModule;
import org.briarproject.bramble.lifecycle.LifecycleModule; import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.plugin.PluginModule; import org.briarproject.bramble.plugin.PluginModule;
@@ -36,6 +37,7 @@ import dagger.Module;
DatabaseExecutorModule.class, DatabaseExecutorModule.class,
EventModule.class, EventModule.class,
IdentityModule.class, IdentityModule.class,
IoModule.class,
KeyAgreementModule.class, KeyAgreementModule.class,
LifecycleModule.class, LifecycleModule.class,
PluginModule.class, PluginModule.class,

View File

@@ -39,7 +39,8 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
@Override @Override
public Group createContactGroup(ClientId clientId, int majorVersion, public Group createContactGroup(ClientId clientId, int majorVersion,
Contact contact, AuthorId local) { Contact contact) {
AuthorId local = contact.getLocalAuthorId();
AuthorId remote = contact.getAuthor().getId(); AuthorId remote = contact.getAuthor().getId();
byte[] descriptor = createGroupDescriptor(local, remote); byte[] descriptor = createGroupDescriptor(local, remote);
return groupFactory.createGroup(clientId, majorVersion, descriptor); return groupFactory.createGroup(clientId, majorVersion, descriptor);

View File

@@ -2,11 +2,10 @@ package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.ContactExchangeListener;
import org.briarproject.bramble.api.contact.ContactExchangeTask; import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactExchangeFailedEvent;
import org.briarproject.bramble.api.contact.event.ContactExchangeSucceededEvent;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.data.BdfDictionary; import org.briarproject.bramble.api.data.BdfDictionary;
@@ -14,7 +13,6 @@ import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -65,7 +63,6 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final RecordReaderFactory recordReaderFactory; private final RecordReaderFactory recordReaderFactory;
private final RecordWriterFactory recordWriterFactory; private final RecordWriterFactory recordWriterFactory;
private final EventBus eventBus;
private final Clock clock; private final Clock clock;
private final ConnectionManager connectionManager; private final ConnectionManager connectionManager;
private final ContactManager contactManager; private final ContactManager contactManager;
@@ -74,18 +71,18 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
private final StreamReaderFactory streamReaderFactory; private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory; private final StreamWriterFactory streamWriterFactory;
private volatile ContactExchangeListener listener;
private volatile LocalAuthor localAuthor; private volatile LocalAuthor localAuthor;
private volatile DuplexTransportConnection conn; private volatile DuplexTransportConnection conn;
private volatile TransportId transportId; private volatile TransportId transportId;
private volatile SecretKey masterKey; private volatile SecretKey masterSecret;
private volatile boolean alice; private volatile boolean alice;
@Inject @Inject
ContactExchangeTaskImpl(DatabaseComponent db, ClientHelper clientHelper, ContactExchangeTaskImpl(DatabaseComponent db, ClientHelper clientHelper,
RecordReaderFactory recordReaderFactory, RecordReaderFactory recordReaderFactory,
RecordWriterFactory recordWriterFactory, EventBus eventBus, RecordWriterFactory recordWriterFactory, Clock clock,
Clock clock, ConnectionManager connectionManager, ConnectionManager connectionManager, ContactManager contactManager,
ContactManager contactManager,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
CryptoComponent crypto, StreamReaderFactory streamReaderFactory, CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory) { StreamWriterFactory streamWriterFactory) {
@@ -93,7 +90,6 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.recordReaderFactory = recordReaderFactory; this.recordReaderFactory = recordReaderFactory;
this.recordWriterFactory = recordWriterFactory; this.recordWriterFactory = recordWriterFactory;
this.eventBus = eventBus;
this.clock = clock; this.clock = clock;
this.connectionManager = connectionManager; this.connectionManager = connectionManager;
this.contactManager = contactManager; this.contactManager = contactManager;
@@ -104,13 +100,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} }
@Override @Override
public void startExchange(LocalAuthor localAuthor, SecretKey masterKey, public void startExchange(ContactExchangeListener listener,
LocalAuthor localAuthor, SecretKey masterSecret,
DuplexTransportConnection conn, TransportId transportId, DuplexTransportConnection conn, TransportId transportId,
boolean alice) { boolean alice) {
this.listener = listener;
this.localAuthor = localAuthor; this.localAuthor = localAuthor;
this.conn = conn; this.conn = conn;
this.transportId = transportId; this.transportId = transportId;
this.masterKey = masterKey; this.masterSecret = masterSecret;
this.alice = alice; this.alice = alice;
start(); start();
} }
@@ -125,8 +123,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
out = conn.getWriter().getOutputStream(); out = conn.getWriter().getOutputStream();
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
listener.contactExchangeFailed();
tryToClose(conn); tryToClose(conn);
eventBus.broadcast(new ContactExchangeFailedEvent());
return; return;
} }
@@ -136,15 +134,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
localProperties = transportPropertyManager.getLocalProperties(); localProperties = transportPropertyManager.getLocalProperties();
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
eventBus.broadcast(new ContactExchangeFailedEvent()); listener.contactExchangeFailed();
tryToClose(conn); tryToClose(conn);
return; return;
} }
// Derive the header keys for the transport streams // Derive the header keys for the transport streams
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, masterKey, SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL,
new byte[] {PROTOCOL_VERSION}); masterSecret, new byte[] {PROTOCOL_VERSION});
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterKey, SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
// Create the readers // Create the readers
@@ -163,9 +161,9 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
.createRecordWriter(streamWriter.getOutputStream()); .createRecordWriter(streamWriter.getOutputStream());
// Derive the nonces to be signed // Derive the nonces to be signed
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterKey, byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterKey, byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
new byte[] {PROTOCOL_VERSION}); new byte[] {PROTOCOL_VERSION});
byte[] localNonce = alice ? aliceNonce : bobNonce; byte[] localNonce = alice ? aliceNonce : bobNonce;
byte[] remoteNonce = alice ? bobNonce : aliceNonce; byte[] remoteNonce = alice ? bobNonce : aliceNonce;
@@ -198,7 +196,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
} }
} catch (IOException e) { } catch (IOException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
eventBus.broadcast(new ContactExchangeFailedEvent()); listener.contactExchangeFailed();
tryToClose(conn); tryToClose(conn);
return; return;
} }
@@ -206,7 +204,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
// Verify the contact's signature // Verify the contact's signature
if (!verify(remoteInfo.author, remoteNonce, remoteInfo.signature)) { if (!verify(remoteInfo.author, remoteNonce, remoteInfo.signature)) {
LOG.warning("Invalid signature"); LOG.warning("Invalid signature");
eventBus.broadcast(new ContactExchangeFailedEvent()); listener.contactExchangeFailed();
tryToClose(conn); tryToClose(conn);
return; return;
} }
@@ -223,17 +221,15 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
conn); conn);
// Pseudonym exchange succeeded // Pseudonym exchange succeeded
LOG.info("Pseudonym exchange succeeded"); LOG.info("Pseudonym exchange succeeded");
eventBus.broadcast( listener.contactExchangeSucceeded(remoteInfo.author);
new ContactExchangeSucceededEvent(remoteInfo.author));
} catch (ContactExistsException e) { } catch (ContactExistsException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
tryToClose(conn); tryToClose(conn);
eventBus.broadcast( listener.duplicateContact(remoteInfo.author);
new ContactExchangeFailedEvent(remoteInfo.author));
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); logException(LOG, WARNING, e);
tryToClose(conn); tryToClose(conn);
eventBus.broadcast(new ContactExchangeFailedEvent()); listener.contactExchangeFailed();
} }
} }
@@ -293,7 +289,8 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
throws DbException { throws DbException {
return db.transactionWithResult(false, txn -> { return db.transactionWithResult(false, txn -> {
ContactId contactId = contactManager.addContact(txn, remoteAuthor, ContactId contactId = contactManager.addContact(txn, remoteAuthor,
masterKey, timestamp, alice, true, true); localAuthor.getId(), masterSecret, timestamp, alice,
true, true);
transportPropertyManager.addRemoteProperties(txn, contactId, transportPropertyManager.addRemoteProperties(txn, contactId,
remoteProperties); remoteProperties);
return contactId; return contactId;

View File

@@ -3,11 +3,10 @@ package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
@@ -17,20 +16,16 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.contact.PendingContactState.WAITING_FOR_CONNECTION;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
@@ -41,12 +36,6 @@ import static org.briarproject.bramble.util.StringUtils.toUtf8;
@NotNullByDefault @NotNullByDefault
class ContactManagerImpl implements ContactManager { class ContactManagerImpl implements ContactManager {
private static final int LINK_LENGTH = 64;
private static final String REMOTE_CONTACT_LINK =
"briar://" + getRandomBase32String(LINK_LENGTH);
private static final Pattern LINK_REGEX =
Pattern.compile("(briar://)?([a-z2-7]{" + LINK_LENGTH + "})");
private final DatabaseComponent db; private final DatabaseComponent db;
private final KeyManager keyManager; private final KeyManager keyManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
@@ -67,94 +56,66 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public ContactId addContact(Transaction txn, Author a, SecretKey rootKey, public ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
keyManager.addContact(txn, c, master, timestamp, alice, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Author remote, AuthorId local, SecretKey master,
long timestamp, boolean alice, boolean verified, boolean active) long timestamp, boolean alice, boolean verified, boolean active)
throws DbException { throws DbException {
ContactId c = db.addContact(txn, a, verified);
keyManager.addContact(txn, c, rootKey, timestamp, alice, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Transaction txn, Author a, boolean verified)
throws DbException {
ContactId c = db.addContact(txn, a, verified);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Author a, SecretKey rootKey, long timestamp,
boolean alice, boolean verified, boolean active)
throws DbException {
return db.transactionWithResult(false, txn -> return db.transactionWithResult(false, txn ->
addContact(txn, a, rootKey, timestamp, alice, addContact(txn, remote, local, master, timestamp, alice,
verified, active)); verified, active));
} }
@Override
public String getRemoteContactLink() {
// TODO replace with real implementation
return REMOTE_CONTACT_LINK;
}
@SuppressWarnings("SameParameterValue")
private static String getRandomBase32String(int length) {
Random random = new Random();
char[] c = new char[length];
for (int i = 0; i < length; i++) {
int character = random.nextInt(32);
if (character < 26) c[i] = (char) ('a' + character);
else c[i] = (char) ('2' + (character - 26));
}
return new String(c);
}
@Override
public boolean isValidRemoteContactLink(String link) {
return LINK_REGEX.matcher(link).matches();
}
@Override
public PendingContact addRemoteContactRequest(String link, String alias) {
// TODO replace with real implementation
PendingContactId id = new PendingContactId(link.getBytes());
return new PendingContact(id, new byte[MAX_PUBLIC_KEY_LENGTH], alias,
WAITING_FOR_CONNECTION, System.currentTimeMillis());
}
@Override
public Collection<PendingContact> getPendingContacts() {
// TODO replace with real implementation
return emptyList();
}
@Override
public void removePendingContact(PendingContact pendingContact) {
// TODO replace with real implementation
}
@Override @Override
public Contact getContact(ContactId c) throws DbException { public Contact getContact(ContactId c) throws DbException {
return db.transactionWithResult(true, txn -> db.getContact(txn, c)); return db.transactionWithResult(true, txn -> db.getContact(txn, c));
} }
@Override @Override
public Contact getContact(AuthorId a) throws DbException { public Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
return db.transactionWithResult(true, txn -> getContact(txn, a)); throws DbException {
return db.transactionWithResult(true, txn ->
getContact(txn, remoteAuthorId, localAuthorId));
} }
@Override @Override
public Contact getContact(Transaction txn, AuthorId a) throws DbException { public Contact getContact(Transaction txn, AuthorId remoteAuthorId,
return db.getContact(txn, a); AuthorId localAuthorId) throws DbException {
Collection<Contact> contacts =
db.getContactsByAuthorId(txn, remoteAuthorId);
for (Contact c : contacts) {
if (c.getLocalAuthorId().equals(localAuthorId)) {
return c;
}
}
throw new NoSuchContactException();
} }
@Override @Override
public Collection<Contact> getContacts() throws DbException { public Collection<Contact> getActiveContacts() throws DbException {
return db.transactionWithResult(true, db::getContacts); Collection<Contact> contacts =
db.transactionWithResult(true, db::getContacts);
List<Contact> active = new ArrayList<>(contacts.size());
for (Contact c : contacts) if (c.isActive()) active.add(c);
return active;
} }
@Override @Override
@@ -162,6 +123,12 @@ class ContactManagerImpl implements ContactManager {
db.transaction(false, txn -> removeContact(txn, c)); db.transaction(false, txn -> removeContact(txn, c));
} }
@Override
public void setContactActive(Transaction txn, ContactId c, boolean active)
throws DbException {
db.setContactActive(txn, c, active);
}
@Override @Override
public void setContactAlias(Transaction txn, ContactId c, public void setContactAlias(Transaction txn, ContactId c,
@Nullable String alias) throws DbException { @Nullable String alias) throws DbException {
@@ -180,14 +147,16 @@ class ContactManagerImpl implements ContactManager {
} }
@Override @Override
public boolean contactExists(Transaction txn, AuthorId a) public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
throws DbException { AuthorId localAuthorId) throws DbException {
return db.containsContact(txn, a); return db.containsContact(txn, remoteAuthorId, localAuthorId);
} }
@Override @Override
public boolean contactExists(AuthorId a) throws DbException { public boolean contactExists(AuthorId remoteAuthorId,
return db.transactionWithResult(true, txn -> contactExists(txn, a)); AuthorId localAuthorId) throws DbException {
return db.transactionWithResult(true, txn ->
contactExists(txn, remoteAuthorId, localAuthorId));
} }
@Override @Override
@@ -209,12 +178,12 @@ class ContactManagerImpl implements ContactManager {
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn); LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
if (localAuthor.getId().equals(authorId)) if (localAuthor.getId().equals(authorId))
return new AuthorInfo(OURSELVES); return new AuthorInfo(OURSELVES);
if (db.containsContact(txn, authorId)) { Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
Contact c = db.getContact(txn, authorId); if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN);
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias()); if (contacts.size() > 1) throw new AssertionError();
else return new AuthorInfo(UNVERIFIED, c.getAlias()); Contact c = contacts.iterator().next();
} if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
return new AuthorInfo(UNKNOWN); else return new AuthorInfo(UNVERIFIED, c.getAlias());
} }
} }

View File

@@ -4,22 +4,18 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto; import org.briarproject.bramble.api.crypto.TransportCrypto;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys; 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 org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.digests.Blake2bDigest; import org.spongycastle.crypto.digests.Blake2bDigest;
import javax.inject.Inject; import javax.inject.Inject;
import static java.lang.System.arraycopy;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HANDSHAKE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HANDSHAKE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ALICE_HEADER_LABEL; 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.ALICE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HANDSHAKE_HEADER_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HANDSHAKE_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.BOB_HEADER_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.BOB_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL; import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
@@ -28,9 +24,6 @@ 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.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.writeUint16;
import static org.briarproject.bramble.util.ByteUtils.writeUint64;
import static org.briarproject.bramble.util.StringUtils.toUtf8;
class TransportCryptoImpl implements TransportCrypto { class TransportCryptoImpl implements TransportCrypto {
@@ -43,44 +36,45 @@ class TransportCryptoImpl implements TransportCrypto {
@Override @Override
public TransportKeys deriveTransportKeys(TransportId t, public TransportKeys deriveTransportKeys(TransportId t,
SecretKey rootKey, long timePeriod, boolean weAreAlice, SecretKey master, long rotationPeriod, boolean alice,
boolean active) { boolean active) {
// Keys for the previous period are derived from the root key // Keys for the previous period are derived from the master secret
SecretKey inTagPrev = deriveTagKey(rootKey, t, !weAreAlice); SecretKey inTagPrev = deriveTagKey(master, t, !alice);
SecretKey inHeaderPrev = deriveHeaderKey(rootKey, t, !weAreAlice); SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
SecretKey outTagPrev = deriveTagKey(rootKey, t, weAreAlice); SecretKey outTagPrev = deriveTagKey(master, t, alice);
SecretKey outHeaderPrev = deriveHeaderKey(rootKey, t, weAreAlice); SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
// Derive the keys for the current and next periods // Derive the keys for the current and next periods
SecretKey inTagCurr = rotateKey(inTagPrev, timePeriod); SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, timePeriod); SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
SecretKey inTagNext = rotateKey(inTagCurr, timePeriod + 1); SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
SecretKey inHeaderNext = rotateKey(inHeaderCurr, timePeriod + 1); SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
SecretKey outTagCurr = rotateKey(outTagPrev, timePeriod); SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, timePeriod); SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
// Initialise the reordering windows and stream counters // Initialise the reordering windows and stream counters
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev, IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
timePeriod - 1); rotationPeriod - 1);
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr, IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
timePeriod); rotationPeriod);
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext, IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
timePeriod + 1); rotationPeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr, OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
timePeriod, active); rotationPeriod, active);
// Collect and return the keys // Collect and return the keys
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr); return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
} }
@Override @Override
public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) { public TransportKeys rotateTransportKeys(TransportKeys k,
if (k.getTimePeriod() >= timePeriod) return k; long rotationPeriod) {
if (k.getRotationPeriod() >= rotationPeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys(); IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys(); IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys(); IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys(); OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getTimePeriod(); long startPeriod = outCurr.getRotationPeriod();
boolean active = outCurr.isActive(); boolean active = outCurr.isActive();
// Rotate the keys // Rotate the keys
for (long p = startPeriod + 1; p <= timePeriod; p++) { for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
inPrev = inCurr; inPrev = inCurr;
inCurr = inNext; inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1); SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
@@ -95,117 +89,24 @@ class TransportCryptoImpl implements TransportCrypto {
outCurr); outCurr);
} }
private SecretKey rotateKey(SecretKey k, long timePeriod) { private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
byte[] period = new byte[INT_64_BYTES]; byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0); ByteUtils.writeUint64(rotationPeriod, period, 0);
return crypto.deriveKey(ROTATE_LABEL, k, period); return crypto.deriveKey(ROTATE_LABEL, k, period);
} }
private SecretKey deriveTagKey(SecretKey rootKey, TransportId t, private SecretKey deriveTagKey(SecretKey master, TransportId t,
boolean keyBelongsToAlice) { boolean alice) {
String label = keyBelongsToAlice ? ALICE_TAG_LABEL : BOB_TAG_LABEL; String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
byte[] id = toUtf8(t.getString()); byte[] id = StringUtils.toUtf8(t.getString());
return crypto.deriveKey(label, rootKey, id); return crypto.deriveKey(label, master, id);
} }
private SecretKey deriveHeaderKey(SecretKey rootKey, TransportId t, private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
boolean keyBelongsToAlice) { boolean alice) {
String label = keyBelongsToAlice ? ALICE_HEADER_LABEL : String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL;
BOB_HEADER_LABEL; byte[] id = StringUtils.toUtf8(t.getString());
byte[] id = toUtf8(t.getString()); return crypto.deriveKey(label, master, id);
return crypto.deriveKey(label, rootKey, id);
}
@Override
public HandshakeKeys deriveHandshakeKeys(TransportId t, SecretKey rootKey,
long timePeriod, boolean weAreAlice) {
if (timePeriod < 1) throw new IllegalArgumentException();
IncomingKeys inPrev = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod - 1);
IncomingKeys inCurr = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr, rootKey,
weAreAlice);
}
private IncomingKeys deriveIncomingHandshakeKeys(TransportId t,
SecretKey rootKey, boolean weAreAlice, long timePeriod) {
SecretKey tag = deriveHandshakeTagKey(t, rootKey, !weAreAlice,
timePeriod);
SecretKey header = deriveHandshakeHeaderKey(t, rootKey, !weAreAlice,
timePeriod);
return new IncomingKeys(tag, header, timePeriod);
}
private OutgoingKeys deriveOutgoingHandshakeKeys(TransportId t,
SecretKey rootKey, boolean weAreAlice, long timePeriod) {
SecretKey tag = deriveHandshakeTagKey(t, rootKey, weAreAlice,
timePeriod);
SecretKey header = deriveHandshakeHeaderKey(t, rootKey, weAreAlice,
timePeriod);
return new OutgoingKeys(tag, header, timePeriod, true);
}
private SecretKey deriveHandshakeTagKey(TransportId t, SecretKey rootKey,
boolean keyBelongsToAlice, long timePeriod) {
String label = keyBelongsToAlice ? ALICE_HANDSHAKE_TAG_LABEL :
BOB_HANDSHAKE_TAG_LABEL;
byte[] id = toUtf8(t.getString());
byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0);
return crypto.deriveKey(label, rootKey, id, period);
}
private SecretKey deriveHandshakeHeaderKey(TransportId t, SecretKey rootKey,
boolean keyBelongsToAlice, long timePeriod) {
String label = keyBelongsToAlice ? ALICE_HANDSHAKE_HEADER_LABEL :
BOB_HANDSHAKE_HEADER_LABEL;
byte[] id = toUtf8(t.getString());
byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0);
return crypto.deriveKey(label, rootKey, id, period);
}
@Override
public HandshakeKeys updateHandshakeKeys(HandshakeKeys k, long timePeriod) {
long elapsed = timePeriod - k.getTimePeriod();
TransportId t = k.getTransportId();
SecretKey rootKey = k.getRootKey();
boolean weAreAlice = k.isAlice();
if (elapsed <= 0) {
// The keys are for the given period or later - don't update them
return k;
} else if (elapsed == 1) {
// The keys are one period old - shift by one period, keeping the
// reordering windows for keys we retain
IncomingKeys inPrev = k.getCurrentIncomingKeys();
IncomingKeys inCurr = k.getNextIncomingKeys();
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice);
} else if (elapsed == 2) {
// The keys are two periods old - shift by two periods, keeping
// the reordering windows for keys we retain
IncomingKeys inPrev = k.getNextIncomingKeys();
IncomingKeys inCurr = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
IncomingKeys inNext = deriveIncomingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod + 1);
OutgoingKeys outCurr = deriveOutgoingHandshakeKeys(t, rootKey,
weAreAlice, timePeriod);
return new HandshakeKeys(t, inPrev, inCurr, inNext, outCurr,
rootKey, weAreAlice);
} else {
// The keys are more than two periods old - derive fresh keys
return deriveHandshakeKeys(t, rootKey, timePeriod, weAreAlice);
}
} }
@Override @Override
@@ -224,14 +125,14 @@ class TransportCryptoImpl implements TransportCrypto {
// The input is the protocol version as a 16-bit integer, followed by // The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer // the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES]; byte[] protocolVersionBytes = new byte[INT_16_BYTES];
writeUint16(protocolVersion, protocolVersionBytes, 0); ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length); prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES]; byte[] streamNumberBytes = new byte[INT_64_BYTES];
writeUint64(streamNumber, streamNumberBytes, 0); ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length); prf.update(streamNumberBytes, 0, streamNumberBytes.length);
byte[] mac = new byte[macLength]; byte[] mac = new byte[macLength];
prf.doFinal(mac, 0); prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC // The output is the first TAG_LENGTH bytes of the MAC
arraycopy(mac, 0, tag, 0, TAG_LENGTH); System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
} }
} }

View File

@@ -0,0 +1,29 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.io.BlockSource;
import org.briarproject.bramble.api.sync.MessageId;
import javax.inject.Inject;
class BlockSourceImpl implements BlockSource {
private final DatabaseComponent db;
@Inject
BlockSourceImpl(DatabaseComponent db) {
this.db = db;
}
@Override
public int getBlockCount(MessageId m) throws DbException {
return db.transactionWithResult(true, txn -> db.getBlockCount(txn, m));
}
@Override
public byte[] getBlock(MessageId m, int blockNumber) throws DbException {
return db.transactionWithResult(true, txn ->
db.getBlock(txn, m, blockNumber));
}
}

View File

@@ -2,13 +2,9 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DataTooNewException; import org.briarproject.bramble.api.db.DataTooNewException;
import org.briarproject.bramble.api.db.DataTooOldException; import org.briarproject.bramble.api.db.DataTooOldException;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MessageDeletedException; import org.briarproject.bramble.api.db.MessageDeletedException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
@@ -27,11 +23,8 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus; import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.HandshakeKeySet; import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection; import java.util.Collection;
@@ -40,14 +33,11 @@ import java.util.Map;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* A low-level interface to the database ({@link DatabaseComponent} provides a * A low-level interface to the database (DatabaseComponent provides a
* high-level interface). * high-level interface). Most operations take a transaction argument, which is
* <p/> * obtained by calling {@link #startTransaction()}. Every transaction must be
* Most operations take a transaction argument, which is obtained by calling * terminated by calling either {@link #abortTransaction(Object) abortTransaction(T)} or
* {@link #startTransaction()}. Every transaction must be terminated by calling * {@link #commitTransaction(Object) commitTransaction(T)}, even if an exception is thrown.
* either {@link #abortTransaction(Object) abortTransaction(T)} or
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an
* exception is thrown.
*/ */
@NotNullByDefault @NotNullByDefault
interface Database<T> { interface Database<T> {
@@ -87,10 +77,11 @@ interface Database<T> {
void commitTransaction(T txn) throws DbException; void commitTransaction(T txn) throws DbException;
/** /**
* Stores a contact with the given pseudonym and returns an ID for the * Stores a contact associated with the given local and remote pseudonyms,
* contact. * and returns an ID for the contact.
*/ */
ContactId addContact(T txn, Author a, boolean verified) throws DbException; ContactId addContact(T txn, Author remote, AuthorId local, boolean verified,
boolean active) throws DbException;
/** /**
* Stores a group. * Stores a group.
@@ -104,20 +95,6 @@ interface Database<T> {
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared) void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
throws DbException; throws DbException;
/**
* Stores the given handshake keys for the given contact and returns a
* key set ID.
*/
HandshakeKeySetId addHandshakeKeys(T txn, ContactId c, HandshakeKeys k)
throws DbException;
/**
* Stores the given handshake keys for the given pending contact and
* returns a key set ID.
*/
HandshakeKeySetId addHandshakeKeys(T txn, PendingContactId p,
HandshakeKeys k) throws DbException;
/** /**
* Stores a local pseudonym. * Stores a local pseudonym.
*/ */
@@ -144,11 +121,6 @@ interface Database<T> {
*/ */
void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException; void addOfferedMessage(T txn, ContactId c, MessageId m) throws DbException;
/**
* Stores a pending contact.
*/
void addPendingContact(T txn, PendingContact p) throws DbException;
/** /**
* Stores a transport. * Stores a transport.
*/ */
@@ -159,15 +131,17 @@ interface Database<T> {
* Stores the given transport keys for the given contact and returns a * Stores the given transport keys for the given contact and returns a
* key set ID. * key set ID.
*/ */
TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k) KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException; throws DbException;
/** /**
* Returns true if the database contains the given contact. * Returns true if the database contains the given contact for the given
* local pseudonym.
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
boolean containsContact(T txn, AuthorId a) throws DbException; boolean containsContact(T txn, AuthorId remote, AuthorId local)
throws DbException;
/** /**
* Returns true if the database contains the given contact. * Returns true if the database contains the given contact.
@@ -197,14 +171,6 @@ interface Database<T> {
*/ */
boolean containsMessage(T txn, MessageId m) throws DbException; boolean containsMessage(T txn, MessageId m) throws DbException;
/**
* Returns true if the database contains the given pending contact.
* <p/>
* Read-only.
*/
boolean containsPendingContact(T txn, PendingContactId p)
throws DbException;
/** /**
* Returns true if the database contains the given transport. * Returns true if the database contains the given transport.
* <p/> * <p/>
@@ -249,13 +215,6 @@ interface Database<T> {
*/ */
Contact getContact(T txn, ContactId c) throws DbException; Contact getContact(T txn, ContactId c) throws DbException;
/**
* Returns the contact with the given author ID.
* <p/>
* Read-only.
*/
Contact getContact(T txn, AuthorId a) throws DbException;
/** /**
* Returns all contacts. * Returns all contacts.
* <p/> * <p/>
@@ -263,6 +222,28 @@ interface Database<T> {
*/ */
Collection<Contact> getContacts(T txn) throws DbException; Collection<Contact> getContacts(T txn) throws DbException;
/**
* Returns a possibly empty collection of contacts with the given author ID.
* <p/>
* Read-only.
*/
Collection<Contact> getContactsByAuthorId(T txn, AuthorId remote)
throws DbException;
/**
* Returns all contacts associated with the given local pseudonym.
* <p/>
* Read-only.
*/
Collection<ContactId> getContacts(T txn, AuthorId a) throws DbException;
/**
* Returns the amount of free storage space available to the database, in
* bytes. This is based on the minimum of the space available on the device
* where the database is stored and the database's configured size.
*/
long getFreeSpace() throws DbException;
/** /**
* Returns the group with the given ID. * Returns the group with the given ID.
* <p/> * <p/>
@@ -303,14 +284,6 @@ interface Database<T> {
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g) Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
throws DbException; throws DbException;
/**
* Returns all handshake keys for the given transport.
* <p/>
* Read-only.
*/
Collection<HandshakeKeySet> getHandshakeKeys(T txn, TransportId t)
throws DbException;
/** /**
* Returns the local pseudonym with the given ID. * Returns the local pseudonym with the given ID.
* <p/> * <p/>
@@ -501,13 +474,6 @@ interface Database<T> {
*/ */
long getNextSendTime(T txn, ContactId c) throws DbException; long getNextSendTime(T txn, ContactId c) throws DbException;
/**
* Returns all pending contacts.
* <p/>
* Read-only.
*/
Collection<PendingContact> getPendingContacts(T txn) throws DbException;
/** /**
* Returns the IDs of some messages that are eligible to be sent to the * Returns the IDs of some messages that are eligible to be sent to the
* given contact and have been requested by the contact, up to the given * given contact and have been requested by the contact, up to the given
@@ -530,19 +496,13 @@ interface Database<T> {
* <p/> * <p/>
* Read-only. * Read-only.
*/ */
Collection<TransportKeySet> getTransportKeys(T txn, TransportId t) Collection<KeySet> getTransportKeys(T txn, TransportId t)
throws DbException;
/**
* Increments the outgoing stream counter for the given handshake keys.
*/
void incrementStreamCounter(T txn, TransportId t, HandshakeKeySetId k)
throws DbException; throws DbException;
/** /**
* Increments the outgoing stream counter for the given transport keys. * Increments the outgoing stream counter for the given transport keys.
*/ */
void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k) void incrementStreamCounter(T txn, TransportId t, KeySetId k)
throws DbException; throws DbException;
/** /**
@@ -611,12 +571,6 @@ interface Database<T> {
void removeGroupVisibility(T txn, ContactId c, GroupId g) void removeGroupVisibility(T txn, ContactId c, GroupId g)
throws DbException; throws DbException;
/**
* Removes the given handshake keys from the database.
*/
void removeHandshakeKeys(T txn, TransportId t, HandshakeKeySetId k)
throws DbException;
/** /**
* Removes a local pseudonym (and all associated state) from the database. * Removes a local pseudonym (and all associated state) from the database.
*/ */
@@ -634,11 +588,6 @@ interface Database<T> {
void removeOfferedMessages(T txn, ContactId c, void removeOfferedMessages(T txn, ContactId c,
Collection<MessageId> requested) throws DbException; Collection<MessageId> requested) throws DbException;
/**
* Removes a pending contact (and all associated state) from the database.
*/
void removePendingContact(T txn, PendingContactId p) throws DbException;
/** /**
* Removes a transport (and all associated state) from the database. * Removes a transport (and all associated state) from the database.
*/ */
@@ -647,7 +596,7 @@ interface Database<T> {
/** /**
* Removes the given transport keys from the database. * Removes the given transport keys from the database.
*/ */
void removeTransportKeys(T txn, TransportId t, TransportKeySetId k) void removeTransportKeys(T txn, TransportId t, KeySetId k)
throws DbException; throws DbException;
/** /**
@@ -661,6 +610,12 @@ interface Database<T> {
*/ */
void setContactVerified(T txn, ContactId c) throws DbException; void setContactVerified(T txn, ContactId c) throws DbException;
/**
* Marks the given contact as active or inactive.
*/
void setContactActive(T txn, ContactId c, boolean active)
throws DbException;
/** /**
* Sets an alias name for a contact. * Sets an alias name for a contact.
*/ */
@@ -686,29 +641,16 @@ interface Database<T> {
throws DbException; throws DbException;
/** /**
* Sets the state of the given pending contact. * Sets the reordering window for the given key set and transport in the
* given rotation period.
*/ */
void setPendingContactState(T txn, PendingContactId p, void setReorderingWindow(T txn, KeySetId k, TransportId t,
PendingContactState state) throws DbException; long rotationPeriod, long base, byte[] bitmap) throws DbException;
/**
* Sets the reordering window for the given transport key set in the given
* time period.
*/
void setReorderingWindow(T txn, TransportKeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException;
/**
* Sets the reordering window for the given handshake key set in the given
* time period.
*/
void setReorderingWindow(T txn, HandshakeKeySetId k, TransportId t,
long timePeriod, long base, byte[] bitmap) throws DbException;
/** /**
* Marks the given transport keys as usable for outgoing streams. * Marks the given transport keys as usable for outgoing streams.
*/ */
void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k) void setTransportKeysActive(T txn, TransportId t, KeySetId k)
throws DbException; throws DbException;
/** /**
@@ -719,13 +661,8 @@ interface Database<T> {
void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, int maxLatency) void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, int maxLatency)
throws DbException; throws DbException;
/**
* Updates the given handshake keys.
*/
void updateHandshakeKeys(T txn, HandshakeKeySet ks) throws DbException;
/** /**
* Updates the given transport keys following key rotation. * Updates the given transport keys following key rotation.
*/ */
void updateTransportKeys(T txn, TransportKeySet ks) throws DbException; void updateTransportKeys(T txn, KeySet ks) throws DbException;
} }

View File

@@ -2,34 +2,28 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent; import org.briarproject.bramble.api.contact.event.ContactVerifiedEvent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.CommitAction;
import org.briarproject.bramble.api.db.CommitAction.Visitor;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbCallable; import org.briarproject.bramble.api.db.DbCallable;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.DbRunnable; import org.briarproject.bramble.api.db.DbRunnable;
import org.briarproject.bramble.api.db.EventAction;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.MigrationListener; import org.briarproject.bramble.api.db.MigrationListener;
import org.briarproject.bramble.api.db.NoSuchBlockException;
import org.briarproject.bramble.api.db.NoSuchContactException; import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
import org.briarproject.bramble.api.db.NoSuchMessageException; import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
import org.briarproject.bramble.api.db.NoSuchTransportException; import org.briarproject.bramble.api.db.NoSuchTransportException;
import org.briarproject.bramble.api.db.NullableDbCallable; import org.briarproject.bramble.api.db.NullableDbCallable;
import org.briarproject.bramble.api.db.PendingContactExistsException;
import org.briarproject.bramble.api.db.TaskAction;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId; import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
@@ -62,11 +56,8 @@ import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.sync.validation.MessageState; import org.briarproject.bramble.api.sync.validation.MessageState;
import org.briarproject.bramble.api.transport.HandshakeKeySet; import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.HandshakeKeySetId; import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.ArrayList; import java.util.ArrayList;
@@ -74,7 +65,6 @@ import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -103,29 +93,25 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
private final Database<T> db; private final Database<T> db;
private final Class<T> txnClass; private final Class<T> txnClass;
private final EventBus eventBus; private final EventBus eventBus;
private final Executor eventExecutor; private final ShutdownManager shutdown;
private final ShutdownManager shutdownManager;
private final AtomicBoolean closed = new AtomicBoolean(false); private final AtomicBoolean closed = new AtomicBoolean(false);
private final ReentrantReadWriteLock lock = private final ReentrantReadWriteLock lock =
new ReentrantReadWriteLock(true); new ReentrantReadWriteLock(true);
private final Visitor visitor = new CommitActionVisitor();
@Inject @Inject
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus, DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
@EventExecutor Executor eventExecutor, ShutdownManager shutdown) {
ShutdownManager shutdownManager) {
this.db = db; this.db = db;
this.txnClass = txnClass; this.txnClass = txnClass;
this.eventBus = eventBus; this.eventBus = eventBus;
this.eventExecutor = eventExecutor; this.shutdown = shutdown;
this.shutdownManager = shutdownManager;
} }
@Override @Override
public boolean open(SecretKey key, @Nullable MigrationListener listener) public boolean open(SecretKey key, @Nullable MigrationListener listener)
throws DbException { throws DbException {
boolean reopened = db.open(key, listener); boolean reopened = db.open(key, listener);
shutdownManager.addShutdownHook(() -> { shutdown.addShutdownHook(() -> {
try { try {
close(); close();
} catch (DbException e) { } catch (DbException e) {
@@ -175,16 +161,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void endTransaction(Transaction transaction) { public void endTransaction(Transaction transaction) {
try { try {
T txn = txnClass.cast(transaction.unbox()); T txn = txnClass.cast(transaction.unbox());
if (transaction.isCommitted()) { if (!transaction.isCommitted()) db.abortTransaction(txn);
for (CommitAction a : transaction.getActions())
a.accept(visitor);
} else {
db.abortTransaction(txn);
}
} finally { } finally {
if (transaction.isReadOnly()) lock.readLock().unlock(); if (transaction.isReadOnly()) lock.readLock().unlock();
else lock.writeLock().unlock(); else lock.writeLock().unlock();
} }
if (transaction.isCommitted())
for (Event e : transaction.getEvents()) eventBus.broadcast(e);
} }
@Override @Override
@@ -232,16 +215,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public ContactId addContact(Transaction transaction, Author a, public ContactId addContact(Transaction transaction, Author remote,
boolean verified) throws DbException { AuthorId local, boolean verified, boolean active)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (db.containsLocalAuthor(txn, a.getId())) if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException();
if (db.containsLocalAuthor(txn, remote.getId()))
throw new ContactExistsException(); throw new ContactExistsException();
if (db.containsContact(txn, a.getId())) if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException(); throw new ContactExistsException();
ContactId c = db.addContact(txn, a, verified); ContactId c = db.addContact(txn, remote, local, verified, active);
transaction.attach(new ContactAddedEvent(c)); transaction.attach(new ContactAddedEvent(c, active));
if (active) transaction.attach(new ContactStatusChangedEvent(c, true));
return c; return c;
} }
@@ -255,30 +242,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
} }
@Override
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
ContactId c, HandshakeKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addHandshakeKeys(txn, c, k);
}
@Override
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
PendingContactId p, HandshakeKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addHandshakeKeys(txn, p, k);
}
@Override @Override
public void addLocalAuthor(Transaction transaction, LocalAuthor a) public void addLocalAuthor(Transaction transaction, LocalAuthor a)
throws DbException { throws DbException {
@@ -307,16 +270,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.mergeMessageMetadata(txn, m.getId(), meta); db.mergeMessageMetadata(txn, m.getId(), meta);
} }
@Override
public void addPendingContact(Transaction transaction, PendingContact p)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (db.containsPendingContact(txn, p.getId()))
throw new PendingContactExistsException();
db.addPendingContact(txn, p);
}
@Override @Override
public void addTransport(Transaction transaction, TransportId t, public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException { int maxLatency) throws DbException {
@@ -327,8 +280,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public TransportKeySetId addTransportKeys(Transaction transaction, public KeySetId addTransportKeys(Transaction transaction, ContactId c,
ContactId c, TransportKeys k) throws DbException { TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsContact(txn, c)) if (!db.containsContact(txn, c))
@@ -339,10 +292,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public boolean containsContact(Transaction transaction, AuthorId a) public boolean containsContact(Transaction transaction, AuthorId remote,
throws DbException { AuthorId local) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
return db.containsContact(txn, a); if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException();
return db.containsContact(txn, remote, local);
} }
@Override @Override
@@ -359,13 +314,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.containsLocalAuthor(txn, local); return db.containsLocalAuthor(txn, local);
} }
@Override
public boolean containsPendingContact(Transaction transaction,
PendingContactId p) throws DbException {
T txn = unbox(transaction);
return db.containsPendingContact(txn, p);
}
@Override @Override
public void deleteMessage(Transaction transaction, MessageId m) public void deleteMessage(Transaction transaction, MessageId m)
throws DbException { throws DbException {
@@ -473,6 +421,26 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return messages; return messages;
} }
@Override
public int getBlockCount(Transaction transaction, MessageId m)
throws DbException {
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
return 1;
}
@Override
public byte[] getBlock(Transaction transaction, MessageId m,
int blockNumber) throws DbException {
T txn = unbox(transaction);
if (!db.containsMessage(txn, m))
throw new NoSuchMessageException();
if (blockNumber != 0)
throw new NoSuchBlockException();
return db.getMessage(txn, m).getBody();
}
@Override @Override
public Contact getContact(Transaction transaction, ContactId c) public Contact getContact(Transaction transaction, ContactId c)
throws DbException { throws DbException {
@@ -482,15 +450,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getContact(txn, c); return db.getContact(txn, c);
} }
@Override
public Contact getContact(Transaction transaction, AuthorId a)
throws DbException {
T txn = unbox(transaction);
if (!db.containsContact(txn, a))
throw new NoSuchContactException();
return db.getContact(txn, a);
}
@Override @Override
public Collection<Contact> getContacts(Transaction transaction) public Collection<Contact> getContacts(Transaction transaction)
throws DbException { throws DbException {
@@ -498,6 +457,22 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getContacts(txn); return db.getContacts(txn);
} }
@Override
public Collection<Contact> getContactsByAuthorId(Transaction transaction,
AuthorId remote) throws DbException {
T txn = unbox(transaction);
return db.getContactsByAuthorId(txn, remote);
}
@Override
public Collection<ContactId> getContacts(Transaction transaction,
AuthorId a) throws DbException {
T txn = unbox(transaction);
if (!db.containsLocalAuthor(txn, a))
throw new NoSuchLocalAuthorException();
return db.getContacts(txn, a);
}
@Override @Override
public Group getGroup(Transaction transaction, GroupId g) public Group getGroup(Transaction transaction, GroupId g)
throws DbException { throws DbException {
@@ -532,15 +507,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getGroupVisibility(txn, c, g); return db.getGroupVisibility(txn, c, g);
} }
@Override
public Collection<HandshakeKeySet> getHandshakeKeys(Transaction transaction,
TransportId t) throws DbException {
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
return db.getHandshakeKeys(txn, t);
}
@Override @Override
public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a) public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -698,13 +664,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getNextSendTime(txn, c); return db.getNextSendTime(txn, c);
} }
@Override
public Collection<PendingContact> getPendingContacts(
Transaction transaction) throws DbException {
T txn = unbox(transaction);
return db.getPendingContacts(txn);
}
@Override @Override
public Settings getSettings(Transaction transaction, String namespace) public Settings getSettings(Transaction transaction, String namespace)
throws DbException { throws DbException {
@@ -713,7 +672,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public Collection<TransportKeySet> getTransportKeys(Transaction transaction, public Collection<KeySet> getTransportKeys(Transaction transaction,
TransportId t) throws DbException { TransportId t) throws DbException {
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -723,17 +682,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void incrementStreamCounter(Transaction transaction, TransportId t, public void incrementStreamCounter(Transaction transaction, TransportId t,
HandshakeKeySetId k) throws DbException { KeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.incrementStreamCounter(txn, t, k);
}
@Override
public void incrementStreamCounter(Transaction transaction, TransportId t,
TransportKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -882,16 +831,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new GroupVisibilityUpdatedEvent(affected)); transaction.attach(new GroupVisibilityUpdatedEvent(affected));
} }
@Override
public void removeHandshakeKeys(Transaction transaction,
TransportId t, HandshakeKeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.removeHandshakeKeys(txn, t, k);
}
@Override @Override
public void removeLocalAuthor(Transaction transaction, AuthorId a) public void removeLocalAuthor(Transaction transaction, AuthorId a)
throws DbException { throws DbException {
@@ -914,16 +853,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.removeMessage(txn, m); db.removeMessage(txn, m);
} }
@Override
public void removePendingContact(Transaction transaction,
PendingContactId p) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
db.removePendingContact(txn, p);
}
@Override @Override
public void removeTransport(Transaction transaction, TransportId t) public void removeTransport(Transaction transaction, TransportId t)
throws DbException { throws DbException {
@@ -936,7 +865,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override @Override
public void removeTransportKeys(Transaction transaction, public void removeTransportKeys(Transaction transaction,
TransportId t, TransportKeySetId k) throws DbException { TransportId t, KeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -955,6 +884,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
transaction.attach(new ContactVerifiedEvent(c)); transaction.attach(new ContactVerifiedEvent(c));
} }
@Override
public void setContactActive(Transaction transaction, ContactId c,
boolean active) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.setContactActive(txn, c, active);
transaction.attach(new ContactStatusChangedEvent(c, active));
}
@Override @Override
public void setContactAlias(Transaction transaction, ContactId c, public void setContactAlias(Transaction transaction, ContactId c,
@Nullable String alias) throws DbException { @Nullable String alias) throws DbException {
@@ -1024,30 +964,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
@Override @Override
public void setReorderingWindow(Transaction transaction, public void setReorderingWindow(Transaction transaction, KeySetId k,
TransportKeySetId k, TransportId t, long timePeriod, long base, TransportId t, long rotationPeriod, long base, byte[] bitmap)
byte[] bitmap) throws DbException { throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
throw new NoSuchTransportException(); throw new NoSuchTransportException();
db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap); db.setReorderingWindow(txn, k, t, rotationPeriod, base, bitmap);
}
@Override
public void setReorderingWindow(Transaction transaction,
HandshakeKeySetId k, TransportId t, long timePeriod, long base,
byte[] bitmap) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
throw new NoSuchTransportException();
db.setReorderingWindow(txn, k, t, timePeriod, base, bitmap);
} }
@Override @Override
public void setTransportKeysActive(Transaction transaction, TransportId t, public void setTransportKeysActive(Transaction transaction, TransportId t,
TransportKeySetId k) throws DbException { KeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
if (!db.containsTransport(txn, t)) if (!db.containsTransport(txn, t))
@@ -1055,40 +984,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.setTransportKeysActive(txn, t, k); db.setTransportKeysActive(txn, t, k);
} }
@Override
public void updateHandshakeKeys(Transaction transaction,
Collection<HandshakeKeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
for (HandshakeKeySet ks : keys) {
TransportId t = ks.getKeys().getTransportId();
if (db.containsTransport(txn, t))
db.updateHandshakeKeys(txn, ks);
}
}
@Override @Override
public void updateTransportKeys(Transaction transaction, public void updateTransportKeys(Transaction transaction,
Collection<TransportKeySet> keys) throws DbException { Collection<KeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException(); if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction); T txn = unbox(transaction);
for (TransportKeySet ks : keys) { for (KeySet ks : keys) {
TransportId t = ks.getKeys().getTransportId(); TransportId t = ks.getTransportKeys().getTransportId();
if (db.containsTransport(txn, t)) if (db.containsTransport(txn, t))
db.updateTransportKeys(txn, ks); db.updateTransportKeys(txn, ks);
} }
} }
private class CommitActionVisitor implements Visitor {
@Override
public void visit(EventAction a) {
eventBus.broadcast(a.getEvent());
}
@Override
public void visit(TaskAction a) {
eventExecutor.execute(a.getTask());
}
}
} }

View File

@@ -3,13 +3,12 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor; import org.briarproject.bramble.api.io.BlockSource;
import org.briarproject.bramble.api.lifecycle.ShutdownManager; import org.briarproject.bramble.api.lifecycle.ShutdownManager;
import org.briarproject.bramble.api.sync.MessageFactory; import org.briarproject.bramble.api.sync.MessageFactory;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import java.sql.Connection; import java.sql.Connection;
import java.util.concurrent.Executor;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -29,9 +28,13 @@ public class DatabaseModule {
@Provides @Provides
@Singleton @Singleton
DatabaseComponent provideDatabaseComponent(Database<Connection> db, DatabaseComponent provideDatabaseComponent(Database<Connection> db,
EventBus eventBus, @EventExecutor Executor eventExecutor, EventBus eventBus, ShutdownManager shutdown) {
ShutdownManager shutdownManager) {
return new DatabaseComponentImpl<>(db, Connection.class, eventBus, return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
eventExecutor, shutdownManager); shutdown);
}
@Provides
BlockSource provideBlockSource(BlockSourceImpl blockSource) {
return blockSource;
} }
} }

View File

@@ -76,6 +76,30 @@ class H2Database extends JdbcDatabase {
} }
} }
@Override
public long getFreeSpace() {
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 @Override
protected Connection createConnection() throws SQLException { protected Connection createConnection() throws SQLException {
SecretKey key = this.key; SecretKey key = this.key;

View File

@@ -86,6 +86,30 @@ class HyperSqlDatabase extends JdbcDatabase {
} }
} }
@Override
public long getFreeSpace() {
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 @Override
protected Connection createConnection() throws SQLException { protected Connection createConnection() throws SQLException {
SecretKey key = this.key; SecretKey key = this.key;

View File

@@ -17,7 +17,7 @@ class Migration40_41 implements Migration<Connection> {
private final DatabaseTypes dbTypes; private final DatabaseTypes dbTypes;
Migration40_41(DatabaseTypes databaseTypes) { public Migration40_41(DatabaseTypes databaseTypes) {
this.dbTypes = databaseTypes; this.dbTypes = databaseTypes;
} }

View File

@@ -1,97 +0,0 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration41_42 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration41_42.class.getName());
private final DatabaseTypes dbTypes;
Migration41_42(DatabaseTypes dbTypes) {
this.dbTypes = dbTypes;
}
@Override
public int getStartVersion() {
return 41;
}
@Override
public int getEndVersion() {
return 42;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE outgoingKeys"
+ " ALTER COLUMN rotationPeriod"
+ " RENAME TO timePeriod");
s.execute("ALTER TABLE incomingKeys"
+ " ALTER COLUMN rotationPeriod"
+ " RENAME TO timePeriod");
s.execute("ALTER TABLE incomingKeys"
+ " DROP COLUMN contactId");
s.execute(dbTypes.replaceTypes("CREATE TABLE pendingContacts"
+ " (pendingContactId _HASH NOT NULL,"
+ " publicKey _BINARY NOT NULL,"
+ " alias _STRING NOT NULL,"
+ " state INT NOT NULL,"
+ " timestamp BIGINT NOT NULL,"
+ " PRIMARY KEY (pendingContactId))"));
s.execute(dbTypes.replaceTypes("CREATE TABLE outgoingHandshakeKeys"
+ " (transportId _STRING NOT NULL,"
+ " keySetId _COUNTER,"
+ " timePeriod BIGINT NOT NULL,"
+ " contactId INT," // Null if contact is pending
+ " pendingContactId _HASH," // Null if not pending
+ " rootKey _SECRET NOT NULL,"
+ " alice BOOLEAN NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " stream BIGINT NOT NULL,"
+ " PRIMARY KEY (transportId, keySetId),"
+ " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE,"
+ " UNIQUE (keySetId),"
+ " FOREIGN KEY (contactId)"
+ " REFERENCES contacts (contactId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (pendingContactId)"
+ " REFERENCES pendingContacts (pendingContactId)"
+ " ON DELETE CASCADE)"));
s.execute(dbTypes.replaceTypes("CREATE TABLE incomingHandshakeKeys"
+ " (transportId _STRING NOT NULL,"
+ " keySetId INT NOT NULL,"
+ " timePeriod BIGINT NOT NULL,"
+ " tagKey _SECRET NOT NULL,"
+ " headerKey _SECRET NOT NULL,"
+ " base BIGINT NOT NULL,"
+ " bitmap _BINARY NOT NULL,"
+ " periodOffset INT NOT NULL,"
+ " PRIMARY KEY (transportId, keySetId, periodOffset),"
+ " FOREIGN KEY (transportId)"
+ " REFERENCES transports (transportId)"
+ " ON DELETE CASCADE,"
+ " FOREIGN KEY (keySetId)"
+ " REFERENCES outgoingHandshakeKeys (keySetId)"
+ " ON DELETE CASCADE)"));
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -1,52 +0,0 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration42_43 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration42_43.class.getName());
private final DatabaseTypes dbTypes;
Migration42_43(DatabaseTypes dbTypes) {
this.dbTypes = dbTypes;
}
@Override
public int getStartVersion() {
return 42;
}
@Override
public int getEndVersion() {
return 43;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute(dbTypes.replaceTypes("ALTER TABLE localAuthors"
+ " ADD COLUMN handshakePublicKey _BINARY"));
s.execute(dbTypes.replaceTypes("ALTER TABLE localAuthors"
+ " ADD COLUMN handshakePrivateKey _BINARY"));
s.execute(dbTypes.replaceTypes("ALTER TABLE contacts"
+ " ADD COLUMN handshakePublicKey _BINARY"));
s.execute("ALTER TABLE contacts"
+ " DROP COLUMN active");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -1,40 +0,0 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DbException;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
class Migration43_44 implements Migration<Connection> {
private static final Logger LOG = getLogger(Migration43_44.class.getName());
@Override
public int getStartVersion() {
return 43;
}
@Override
public int getEndVersion() {
return 44;
}
@Override
public void migrate(Connection txn) throws DbException {
Statement s = null;
try {
s = txn.createStatement();
s.execute("ALTER TABLE contacts"
+ " DROP COLUMN localAuthorId");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
throw new DbException(e);
}
}
}

View File

@@ -1,32 +0,0 @@
package org.briarproject.bramble.event;
import org.briarproject.bramble.api.event.EventExecutor;
import java.util.concurrent.Executor;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static java.util.concurrent.Executors.newSingleThreadExecutor;
/**
* Default implementation of {@link EventExecutor} that uses a dedicated thread
* to notify listeners of events. Applications may prefer to supply an
* implementation that uses an existing thread, such as the UI thread.
*/
@Module
public class DefaultEventExecutorModule {
@Provides
@Singleton
@EventExecutor
Executor provideEventExecutor() {
return newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
t.setDaemon(true);
return t;
});
}
}

View File

@@ -2,16 +2,13 @@ package org.briarproject.bramble.event;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventExecutor;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
@ThreadSafe @ThreadSafe
@NotNullByDefault @NotNullByDefault
@@ -19,12 +16,6 @@ class EventBusImpl implements EventBus {
private final Collection<EventListener> listeners = private final Collection<EventListener> listeners =
new CopyOnWriteArrayList<>(); new CopyOnWriteArrayList<>();
private final Executor eventExecutor;
@Inject
EventBusImpl(@EventExecutor Executor eventExecutor) {
this.eventExecutor = eventExecutor;
}
@Override @Override
public void addListener(EventListener l) { public void addListener(EventListener l) {
@@ -38,8 +29,6 @@ class EventBusImpl implements EventBus {
@Override @Override
public void broadcast(Event e) { public void broadcast(Event e) {
eventExecutor.execute(() -> { for (EventListener l : listeners) l.eventOccurred(e);
for (EventListener l : listeners) l.eventOccurred(e);
});
} }
} }

View File

@@ -12,7 +12,7 @@ public class EventModule {
@Provides @Provides
@Singleton @Singleton
EventBus provideEventBus(EventBusImpl eventBus) { EventBus provideEventBus() {
return eventBus; return new EventBusImpl();
} }
} }

View File

@@ -0,0 +1,155 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import javax.annotation.Nullable;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import static java.lang.System.arraycopy;
import static java.lang.Thread.currentThread;
/**
* An {@link InputStream} that asynchronously fetches blocks of data on demand.
*/
@ThreadSafe
@NotNullByDefault
abstract class BlockInputStream extends InputStream {
private final int minBufferBytes;
private final BlockingQueue<Buffer> queue = new ArrayBlockingQueue<>(1);
private final Object lock = new Object();
@GuardedBy("lock")
@Nullable
private Buffer buffer = null;
@GuardedBy("lock")
private int offset = 0;
@GuardedBy("lock")
private boolean fetchingBlock = false;
abstract void fetchBlockAsync(int blockNumber);
BlockInputStream(int minBufferBytes) {
this.minBufferBytes = minBufferBytes;
}
@Override
public int read() throws IOException {
synchronized (lock) {
if (!prepareRead()) return -1;
if (buffer == null) throw new AssertionError();
return buffer.data[offset++] & 0xFF;
}
}
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (off < 0 || len < 0 || off + len > b.length)
throw new IllegalArgumentException();
synchronized (lock) {
if (!prepareRead()) return -1;
if (buffer == null) throw new AssertionError();
len = Math.min(len, buffer.length - offset);
if (len < 0) throw new AssertionError();
arraycopy(buffer.data, offset, b, off, len);
offset += len;
return len;
}
}
private boolean prepareRead() throws IOException {
throwExceptionIfNecessary();
if (isEndOfStream()) return false;
if (shouldFetchBlock()) fetchBlockAsync();
waitForBlock();
if (buffer == null) throw new AssertionError();
return offset < buffer.length;
}
@GuardedBy("lock")
private void throwExceptionIfNecessary() throws IOException {
if (buffer != null && buffer.exception != null)
throw new IOException(buffer.exception);
}
@GuardedBy("lock")
private boolean isEndOfStream() {
return buffer != null && offset == buffer.length && !fetchingBlock;
}
@GuardedBy("lock")
private boolean shouldFetchBlock() {
if (fetchingBlock) return false;
if (buffer == null) return true;
if (buffer.length == 0) return false;
return buffer.length - offset < minBufferBytes;
}
@GuardedBy("lock")
private void fetchBlockAsync() {
if (buffer == null) fetchBlockAsync(0);
else fetchBlockAsync(buffer.blockNumber + 1);
fetchingBlock = true;
}
@GuardedBy("lock")
private void waitForBlock() throws IOException {
if (buffer != null && offset < buffer.length) return;
try {
buffer = queue.take();
} catch (InterruptedException e) {
currentThread().interrupt();
throw new InterruptedIOException();
}
fetchingBlock = false;
offset = 0;
throwExceptionIfNecessary();
}
void fetchSucceeded(int blockNumber, byte[] data, int length) {
queue.add(new Buffer(blockNumber, data, length));
}
void fetchFailed(int blockNumber, Exception exception) {
queue.add(new Buffer(blockNumber, exception));
}
private static class Buffer {
private final int blockNumber;
private final byte[] data;
private final int length;
@Nullable
private final Exception exception;
private Buffer(int blockNumber, byte[] data, int length) {
if (length < 0 || length > data.length)
throw new IllegalArgumentException();
this.blockNumber = blockNumber;
this.data = data;
this.length = length;
exception = null;
}
private Buffer(int blockNumber, Exception exception) {
this.blockNumber = blockNumber;
this.exception = exception;
data = new byte[0];
length = 0;
}
}
}

View File

@@ -0,0 +1,53 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.io.BlockSource;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import java.util.concurrent.Executor;
import javax.annotation.concurrent.ThreadSafe;
/**
* A {@link BlockInputStream} that fetches data from a {@link BlockSource}.
*/
@ThreadSafe
@NotNullByDefault
class BlockSourceInputStream extends BlockInputStream {
private final Executor executor;
private final BlockSource blockSource;
private final MessageId messageId;
private volatile int blockCount = -1;
BlockSourceInputStream(int minBufferBytes, Executor executor,
BlockSource blockSource, MessageId messageId) {
super(minBufferBytes);
this.executor = executor;
this.blockSource = blockSource;
this.messageId = messageId;
}
@Override
void fetchBlockAsync(int blockNumber) {
executor.execute(() -> {
try {
if (blockCount == -1) {
blockCount = blockSource.getBlockCount(messageId);
}
if (blockNumber > blockCount) {
fetchFailed(blockNumber, new IllegalArgumentException());
} else if (blockNumber == blockCount) {
fetchSucceeded(blockNumber, new byte[0], 0); // EOF
} else {
byte[] block = blockSource.getBlock(messageId, blockNumber);
fetchSucceeded(blockNumber, block, block.length);
}
} catch (DbException e) {
fetchFailed(blockNumber, e);
}
});
}
}

View File

@@ -0,0 +1,16 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
import dagger.Module;
import dagger.Provides;
@Module
public class IoModule {
@Provides
MessageInputStreamFactory provideMessageInputStreamFactory(
MessageInputStreamFactoryImpl messageInputStreamFactory) {
return messageInputStreamFactory;
}
}

View File

@@ -0,0 +1,32 @@
package org.briarproject.bramble.io;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.io.BlockSource;
import org.briarproject.bramble.api.io.MessageInputStreamFactory;
import org.briarproject.bramble.api.sync.MessageId;
import java.io.InputStream;
import java.util.concurrent.Executor;
import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_BLOCK_LENGTH;
class MessageInputStreamFactoryImpl implements MessageInputStreamFactory {
private final Executor dbExecutor;
private final BlockSource blockSource;
@Inject
MessageInputStreamFactoryImpl(@DatabaseExecutor Executor dbExecutor,
BlockSource blockSource) {
this.dbExecutor = dbExecutor;
this.blockSource = blockSource;
}
@Override
public InputStream getMessageInputStream(MessageId m) {
return new BlockSourceInputStream(MAX_BLOCK_LENGTH, dbExecutor,
blockSource, m);
}
}

View File

@@ -14,7 +14,7 @@ import java.io.IOException;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
import java.util.Arrays; import java.util.Arrays;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_LABEL; 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.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.SHARED_SECRET_LABEL;
@@ -90,7 +90,7 @@ class KeyAgreementProtocol {
/** /**
* Perform the BQP protocol. * Perform the BQP protocol.
* *
* @return the negotiated master key. * @return the negotiated master secret.
* @throws AbortException when the protocol may have been tampered with. * @throws AbortException when the protocol may have been tampered with.
* @throws IOException for all other other connection errors. * @throws IOException for all other other connection errors.
*/ */
@@ -115,7 +115,7 @@ class KeyAgreementProtocol {
receiveConfirm(s, theirPublicKey); receiveConfirm(s, theirPublicKey);
sendConfirm(s, theirPublicKey); sendConfirm(s, theirPublicKey);
} }
return crypto.deriveKey(MASTER_KEY_LABEL, s); return crypto.deriveKey(MASTER_SECRET_LABEL, s);
} catch (AbortException e) { } catch (AbortException e) {
sendAbort(e.getCause() != null); sendAbort(e.getCause() != null);
throw e; throw e;

View File

@@ -114,9 +114,9 @@ class KeyAgreementTaskImpl extends Thread implements KeyAgreementTask,
keyAgreementCrypto, payloadEncoder, transport, remotePayload, keyAgreementCrypto, payloadEncoder, transport, remotePayload,
localPayload, localKeyPair, alice); localPayload, localKeyPair, alice);
try { try {
SecretKey masterKey = protocol.perform(); SecretKey master = protocol.perform();
KeyAgreementResult result = KeyAgreementResult result =
new KeyAgreementResult(masterKey, transport.getConnection(), new KeyAgreementResult(master, transport.getConnection(),
transport.getTransportId(), alice); transport.getTransportId(), alice);
LOG.info("Finished BQP protocol"); LOG.info("Finished BQP protocol");
// Broadcast result to caller // Broadcast result to caller

View File

@@ -1,7 +1,7 @@
package org.briarproject.bramble.plugin; package org.briarproject.bramble.plugin;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
@@ -80,10 +80,12 @@ class Poller implements EventListener {
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactAddedEvent) { if (e instanceof ContactStatusChangedEvent) {
ContactAddedEvent c = (ContactAddedEvent) e; ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
// Connect to the newly activated contact if (c.isActive()) {
connectToContact(c.getContactId()); // Connect to the newly activated contact
connectToContact(c.getContactId());
}
} else if (e instanceof ConnectionClosedEvent) { } else if (e instanceof ConnectionClosedEvent) {
ConnectionClosedEvent c = (ConnectionClosedEvent) e; ConnectionClosedEvent c = (ConnectionClosedEvent) e;
// Reschedule polling, the polling interval may have decreased // Reschedule polling, the polling interval may have decreased

View File

@@ -17,24 +17,16 @@ public interface CircumventionProvider {
String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"}; String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"};
/** /**
* Countries where obfs4 bridge connection are likely to work. * Countries where vanilla bridge connection are likely to work.
* Should be a subset of {@link #BLOCKED}. * Should be a subset of {@link #BLOCKED}.
*/ */
String[] BRIDGES = { "CN", "IR", "EG", "BY", "TR", "SY", "VE" }; String[] BRIDGES = { "EG", "BY", "TR", "SY", "VE" };
/**
* Countries where obfs4 bridges won't work and meek is needed.
* Should be a subset of {@link #BRIDGES}.
*/
String[] NEEDS_MEEK = {"CN", "IR"};
boolean isTorProbablyBlocked(String countryCode); boolean isTorProbablyBlocked(String countryCode);
boolean doBridgesWork(String countryCode); boolean doBridgesWork(String countryCode);
boolean needsMeek(String countryCode);
@IoExecutor @IoExecutor
List<String> getBridges(boolean meek); List<String> getBridges();
} }

View File

@@ -22,8 +22,6 @@ class CircumventionProviderImpl implements CircumventionProvider {
new HashSet<>(asList(BLOCKED)); new HashSet<>(asList(BLOCKED));
private static final Set<String> BRIDGES_WORK_IN_COUNTRIES = private static final Set<String> BRIDGES_WORK_IN_COUNTRIES =
new HashSet<>(asList(BRIDGES)); new HashSet<>(asList(BRIDGES));
private static final Set<String> BRIDGES_NEED_MEEK =
new HashSet<>(asList(NEEDS_MEEK));
@Nullable @Nullable
private volatile List<String> bridges = null; private volatile List<String> bridges = null;
@@ -42,14 +40,9 @@ class CircumventionProviderImpl implements CircumventionProvider {
return BRIDGES_WORK_IN_COUNTRIES.contains(countryCode); return BRIDGES_WORK_IN_COUNTRIES.contains(countryCode);
} }
@Override
public boolean needsMeek(String countryCode) {
return BRIDGES_NEED_MEEK.contains(countryCode);
}
@Override @Override
@IoExecutor @IoExecutor
public List<String> getBridges(boolean useMeek) { public List<String> getBridges() {
List<String> bridges = this.bridges; List<String> bridges = this.bridges;
if (bridges != null) return new ArrayList<>(bridges); if (bridges != null) return new ArrayList<>(bridges);
@@ -60,8 +53,6 @@ class CircumventionProviderImpl implements CircumventionProvider {
bridges = new ArrayList<>(); bridges = new ArrayList<>();
while (scanner.hasNextLine()) { while (scanner.hasNextLine()) {
String line = scanner.nextLine(); String line = scanner.nextLine();
boolean isMeekBridge = line.startsWith("Bridge meek");
if (useMeek && !isMeekBridge || !useMeek && isMeekBridge) continue;
if (!line.startsWith("#")) bridges.add(line); if (!line.startsWith("#")) bridges.add(line);
} }
scanner.close(); scanner.close();

View File

@@ -69,7 +69,6 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_WITH_BRIDGES;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_ONLY_WHEN_CHARGING;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT; import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_PORT;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3; import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
@@ -470,19 +469,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!enable) callback.transportDisabled(); if (!enable) callback.transportDisabled();
} }
private void enableBridges(boolean enable, boolean needsMeek) private void enableBridges(boolean enable) throws IOException {
throws IOException {
if (enable) { if (enable) {
Collection<String> conf = new ArrayList<>(); Collection<String> conf = new ArrayList<>();
conf.add("UseBridges 1"); conf.add("UseBridges 1");
if (needsMeek) { conf.add("ClientTransportPlugin obfs4 exec " +
conf.add("ClientTransportPlugin meek_lite exec " + obfs4File.getAbsolutePath());
obfs4File.getAbsolutePath()); conf.addAll(circumventionProvider.getBridges());
} else {
conf.add("ClientTransportPlugin obfs4 exec " +
obfs4File.getAbsolutePath());
}
conf.addAll(circumventionProvider.getBridges(needsMeek));
controlConnection.setConf(conf); controlConnection.setConf(conf);
} else { } else {
controlConnection.setConf("UseBridges", "0"); controlConnection.setConf("UseBridges", "0");
@@ -655,10 +648,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (s.getNamespace().equals(ID.getString())) { if (s.getNamespace().equals(ID.getString())) {
LOG.info("Tor settings updated"); LOG.info("Tor settings updated");
settings = s.getSettings(); settings = s.getSettings();
// Works around a bug introduced in Tor 0.3.4.8. // Works around a bug introduced in Tor 0.3.4.8. Could be
// https://trac.torproject.org/projects/tor/ticket/28027 // replaced with callback.transportDisabled() when fixed.
// Could be replaced with callback.transportDisabled()
// when fixed.
disableNetwork(); disableNetwork();
updateConnectionStatus(networkManager.getNetworkStatus(), updateConnectionStatus(networkManager.getNetworkStatus(),
batteryManager.isCharging()); batteryManager.isCharging());
@@ -694,8 +685,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
int network = settings.getInt(PREF_TOR_NETWORK, int network = settings.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_AUTOMATIC); PREF_TOR_NETWORK_AUTOMATIC);
boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true); boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true);
boolean onlyWhenCharging =
settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false);
boolean bridgesWork = circumventionProvider.doBridgesWork(country); boolean bridgesWork = circumventionProvider.doBridgesWork(country);
boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC; boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC;
@@ -710,29 +699,21 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!online) { if (!online) {
LOG.info("Disabling network, device is offline"); LOG.info("Disabling network, device is offline");
enableNetwork(false); enableNetwork(false);
} else if (!charging && onlyWhenCharging) {
LOG.info("Disabling network, device is on battery");
enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_NEVER || } else if (network == PREF_TOR_NETWORK_NEVER ||
(!useMobile && !wifi)) { (!useMobile && !wifi)) {
LOG.info("Disabling network, device is using mobile data"); LOG.info("Disabling network due to setting");
enableNetwork(false); enableNetwork(false);
} else if (automatic && blocked && !bridgesWork) { } else if (automatic && blocked && !bridgesWork) {
LOG.info("Disabling network, country is blocked"); LOG.info("Disabling network, country is blocked");
enableNetwork(false); enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_WITH_BRIDGES || } else if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
(automatic && bridgesWork)) { (automatic && bridgesWork)) {
if (circumventionProvider.needsMeek(country)) { LOG.info("Enabling network, using bridges");
LOG.info("Enabling network, using meek bridges"); enableBridges(true);
enableBridges(true, true);
} else {
LOG.info("Enabling network, using obfs4 bridges");
enableBridges(true, false);
}
enableNetwork(true); enableNetwork(true);
} else { } else {
LOG.info("Enabling network, not using bridges"); LOG.info("Enabling network, not using bridges");
enableBridges(false, false); enableBridges(false);
enableNetwork(true); enableNetwork(true);
} }
if (online && wifi && charging) { if (online && wifi && charging) {

View File

@@ -13,8 +13,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.properties.TransportProperties; import org.briarproject.bramble.api.properties.TransportProperties;
@@ -46,7 +44,6 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final IdentityManager identityManager;
private final ClientVersioningManager clientVersioningManager; private final ClientVersioningManager clientVersioningManager;
private final MetadataParser metadataParser; private final MetadataParser metadataParser;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
@@ -56,13 +53,11 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Inject @Inject
TransportPropertyManagerImpl(DatabaseComponent db, TransportPropertyManagerImpl(DatabaseComponent db,
ClientHelper clientHelper, ClientHelper clientHelper,
IdentityManager identityManager,
ClientVersioningManager clientVersioningManager, ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MetadataParser metadataParser,
ContactGroupFactory contactGroupFactory, Clock clock) { ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.identityManager = identityManager;
this.clientVersioningManager = clientVersioningManager; this.clientVersioningManager = clientVersioningManager;
this.metadataParser = metadataParser; this.metadataParser = metadataParser;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
@@ -82,7 +77,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group to share with the contact // Create a group to share with the contact
Group g = getContactGroup(c, getLocalAuthorId(txn)); Group g = getContactGroup(c);
db.addGroup(txn, g); db.addGroup(txn, g);
// Apply the client's visibility to the contact group // Apply the client's visibility to the contact group
Visibility client = clientVersioningManager.getClientVisibility(txn, Visibility client = clientVersioningManager.getClientVisibility(txn,
@@ -98,14 +93,14 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void removingContact(Transaction txn, Contact c) throws DbException { public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c, getLocalAuthorId(txn))); db.removeGroup(txn, getContactGroup(c));
} }
@Override @Override
public void onClientVisibilityChanging(Transaction txn, Contact c, public void onClientVisibilityChanging(Transaction txn, Contact c,
Visibility v) throws DbException { Visibility v) throws DbException {
// Apply the client's visibility to the contact group // Apply the client's visibility to the contact group
Group g = getContactGroup(c, getLocalAuthorId(txn)); Group g = getContactGroup(c);
db.setGroupVisibility(txn, c.getId(), g.getId(), v); db.setGroupVisibility(txn, c.getId(), g.getId(), v);
} }
@@ -137,7 +132,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public void addRemoteProperties(Transaction txn, ContactId c, public void addRemoteProperties(Transaction txn, ContactId c,
Map<TransportId, TransportProperties> props) throws DbException { Map<TransportId, TransportProperties> props) throws DbException {
Group g = getContactGroup(db.getContact(txn, c), getLocalAuthorId(txn)); Group g = getContactGroup(db.getContact(txn, c));
for (Entry<TransportId, TransportProperties> e : props.entrySet()) { for (Entry<TransportId, TransportProperties> e : props.entrySet()) {
storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 0, storeMessage(txn, g.getId(), e.getKey(), e.getValue(), 0,
false, false); false, false);
@@ -196,16 +191,17 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
TransportId t) throws DbException { TransportId t) throws DbException {
return db.transactionWithResult(true, txn -> { return db.transactionWithResult(true, txn -> {
Map<ContactId, TransportProperties> remote = new HashMap<>(); Map<ContactId, TransportProperties> remote = new HashMap<>();
AuthorId local = getLocalAuthorId(txn);
for (Contact c : db.getContacts(txn)) for (Contact c : db.getContacts(txn))
remote.put(c.getId(), getRemoteProperties(txn, c, local, t)); remote.put(c.getId(), getRemoteProperties(txn, c, t));
return remote; return remote;
}); });
} }
private TransportProperties getRemoteProperties(Transaction txn, Contact c, private TransportProperties getRemoteProperties(Transaction txn, Contact c,
AuthorId local, TransportId t) throws DbException { TransportId t) throws DbException {
Group g = getContactGroup(c, local); // Don't return properties for inactive contacts
if (!c.isActive()) return new TransportProperties();
Group g = getContactGroup(c);
try { try {
// Find the latest remote update // Find the latest remote update
LatestUpdate latest = findLatest(txn, g.getId(), t, false); LatestUpdate latest = findLatest(txn, g.getId(), t, false);
@@ -222,11 +218,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
@Override @Override
public TransportProperties getRemoteProperties(ContactId c, TransportId t) public TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException { throws DbException {
return db.transactionWithResult(true, txn -> { return db.transactionWithResult(true, txn ->
Contact contact = db.getContact(txn ,c); getRemoteProperties(txn, db.getContact(txn, c), t));
AuthorId local = getLocalAuthorId(txn);
return getRemoteProperties(txn, contact, local, t);
});
} }
@Override @Override
@@ -259,9 +252,8 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
if (latest != null) if (latest != null)
db.removeMessage(txn, latest.messageId); db.removeMessage(txn, latest.messageId);
// Store the merged properties in each contact's group // Store the merged properties in each contact's group
AuthorId localAuthorId = getLocalAuthorId(txn);
for (Contact c : db.getContacts(txn)) { for (Contact c : db.getContacts(txn)) {
Group g = getContactGroup(c, localAuthorId); Group g = getContactGroup(c);
latest = findLatest(txn, g.getId(), t, true); latest = findLatest(txn, g.getId(), t, true);
version = latest == null ? 1 : latest.version + 1; version = latest == null ? 1 : latest.version + 1;
storeMessage(txn, g.getId(), t, merged, version, storeMessage(txn, g.getId(), t, merged, version,
@@ -277,13 +269,9 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
} }
} }
private AuthorId getLocalAuthorId(Transaction txn) throws DbException { private Group getContactGroup(Contact c) {
return identityManager.getLocalAuthor(txn).getId();
}
private Group getContactGroup(Contact c, AuthorId local) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c, local); MAJOR_VERSION, c);
} }
private void storeMessage(Transaction txn, GroupId g, TransportId t, private void storeMessage(Transaction txn, GroupId g, TransportId t,

View File

@@ -1,7 +1,9 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
@@ -17,8 +19,8 @@ import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory; import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory; import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -44,6 +46,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
private final Executor dbExecutor; private final Executor dbExecutor;
private final PluginConfig pluginConfig; private final PluginConfig pluginConfig;
private final TransportKeyManagerFactory transportKeyManagerFactory; private final TransportKeyManagerFactory transportKeyManagerFactory;
private final Map<ContactId, Boolean> activeContacts;
private final ConcurrentHashMap<TransportId, TransportKeyManager> managers; private final ConcurrentHashMap<TransportId, TransportKeyManager> managers;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
@@ -55,6 +58,8 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
this.dbExecutor = dbExecutor; this.dbExecutor = dbExecutor;
this.pluginConfig = pluginConfig; this.pluginConfig = pluginConfig;
this.transportKeyManagerFactory = transportKeyManagerFactory; this.transportKeyManagerFactory = transportKeyManagerFactory;
// Use a ConcurrentHashMap as a thread-safe set
activeContacts = new ConcurrentHashMap<>();
managers = new ConcurrentHashMap<>(); managers = new ConcurrentHashMap<>();
} }
@@ -68,6 +73,8 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
transports.put(f.getId(), f.getMaxLatency()); transports.put(f.getId(), f.getMaxLatency());
try { try {
db.transaction(false, txn -> { db.transaction(false, txn -> {
for (Contact c : db.getContacts(txn))
if (c.isActive()) activeContacts.put(c.getId(), true);
for (Entry<TransportId, Integer> e : transports.entrySet()) for (Entry<TransportId, Integer> e : transports.entrySet())
db.addTransport(txn, e.getKey(), e.getValue()); db.addTransport(txn, e.getKey(), e.getValue());
for (Entry<TransportId, Integer> e : transports.entrySet()) { for (Entry<TransportId, Integer> e : transports.entrySet()) {
@@ -88,22 +95,22 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
@Override @Override
public Map<TransportId, TransportKeySetId> addContact(Transaction txn, public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
ContactId c, SecretKey rootKey, long timestamp, boolean alice, SecretKey master, long timestamp, boolean alice, boolean active)
boolean active) throws DbException { throws DbException {
Map<TransportId, TransportKeySetId> ids = new HashMap<>(); Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = e.getValue(); TransportKeyManager m = e.getValue();
ids.put(t, m.addContact(txn, c, rootKey, timestamp, alice, active)); ids.put(t, m.addContact(txn, c, master, timestamp, alice, active));
} }
return ids; return ids;
} }
@Override @Override
public void activateKeys(Transaction txn, Map<TransportId, public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
TransportKeySetId> keys) throws DbException { throws DbException {
for (Entry<TransportId, TransportKeySetId> e : keys.entrySet()) { for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
if (m == null) { if (m == null) {
@@ -123,6 +130,8 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
@Override @Override
public StreamContext getStreamContext(ContactId c, TransportId t) public StreamContext getStreamContext(ContactId c, TransportId t)
throws DbException { throws DbException {
// Don't allow outgoing streams to inactive contacts
if (!activeContacts.containsKey(c)) return null;
TransportKeyManager m = managers.get(t); TransportKeyManager m = managers.get(t);
if (m == null) { if (m == null) {
if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t); if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
@@ -148,10 +157,15 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (e instanceof ContactRemovedEvent) { if (e instanceof ContactRemovedEvent) {
removeContact(((ContactRemovedEvent) e).getContactId()); removeContact(((ContactRemovedEvent) e).getContactId());
} else if (e instanceof ContactStatusChangedEvent) {
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
if (c.isActive()) activeContacts.put(c.getContactId(), true);
else activeContacts.remove(c.getContactId());
} }
} }
private void removeContact(ContactId c) { private void removeContact(ContactId c) {
activeContacts.remove(c);
dbExecutor.execute(() -> { dbExecutor.execute(() -> {
for (TransportKeyManager m : managers.values()) m.removeContact(c); for (TransportKeyManager m : managers.values()) m.removeContact(c);
}); });

View File

@@ -11,18 +11,18 @@ import javax.annotation.concurrent.NotThreadSafe;
class MutableIncomingKeys { class MutableIncomingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long timePeriod; private final long rotationPeriod;
private final ReorderingWindow window; private final ReorderingWindow window;
MutableIncomingKeys(IncomingKeys in) { MutableIncomingKeys(IncomingKeys in) {
tagKey = in.getTagKey(); tagKey = in.getTagKey();
headerKey = in.getHeaderKey(); headerKey = in.getHeaderKey();
timePeriod = in.getTimePeriod(); rotationPeriod = in.getRotationPeriod();
window = new ReorderingWindow(in.getWindowBase(), in.getWindowBitmap()); window = new ReorderingWindow(in.getWindowBase(), in.getWindowBitmap());
} }
IncomingKeys snapshot() { IncomingKeys snapshot() {
return new IncomingKeys(tagKey, headerKey, timePeriod, return new IncomingKeys(tagKey, headerKey, rotationPeriod,
window.getBase(), window.getBitmap()); window.getBase(), window.getBitmap());
} }
@@ -34,8 +34,8 @@ class MutableIncomingKeys {
return headerKey; return headerKey;
} }
long getTimePeriod() { long getRotationPeriod() {
return timePeriod; return rotationPeriod;
} }
ReorderingWindow getWindow() { ReorderingWindow getWindow() {

View File

@@ -1,22 +1,22 @@
package org.briarproject.bramble.transport; package org.briarproject.bramble.transport;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.transport.TransportKeySetId; import org.briarproject.bramble.api.transport.KeySetId;
class MutableKeySet { class MutableKeySet {
private final TransportKeySetId keySetId; private final KeySetId keySetId;
private final ContactId contactId; private final ContactId contactId;
private final MutableTransportKeys transportKeys; private final MutableTransportKeys transportKeys;
MutableKeySet(TransportKeySetId keySetId, ContactId contactId, MutableKeySet(KeySetId keySetId, ContactId contactId,
MutableTransportKeys transportKeys) { MutableTransportKeys transportKeys) {
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
this.transportKeys = transportKeys; this.transportKeys = transportKeys;
} }
TransportKeySetId getKeySetId() { KeySetId getKeySetId() {
return keySetId; return keySetId;
} }

View File

@@ -11,20 +11,20 @@ import javax.annotation.concurrent.NotThreadSafe;
class MutableOutgoingKeys { class MutableOutgoingKeys {
private final SecretKey tagKey, headerKey; private final SecretKey tagKey, headerKey;
private final long timePeriod; private final long rotationPeriod;
private long streamCounter; private long streamCounter;
private boolean active; private boolean active;
MutableOutgoingKeys(OutgoingKeys out) { MutableOutgoingKeys(OutgoingKeys out) {
tagKey = out.getTagKey(); tagKey = out.getTagKey();
headerKey = out.getHeaderKey(); headerKey = out.getHeaderKey();
timePeriod = out.getTimePeriod(); rotationPeriod = out.getRotationPeriod();
streamCounter = out.getStreamCounter(); streamCounter = out.getStreamCounter();
active = out.isActive(); active = out.isActive();
} }
OutgoingKeys snapshot() { OutgoingKeys snapshot() {
return new OutgoingKeys(tagKey, headerKey, timePeriod, return new OutgoingKeys(tagKey, headerKey, rotationPeriod,
streamCounter, active); streamCounter, active);
} }
@@ -36,8 +36,8 @@ class MutableOutgoingKeys {
return headerKey; return headerKey;
} }
long getTimePeriod() { long getRotationPeriod() {
return timePeriod; return rotationPeriod;
} }
long getStreamCounter() { long getStreamCounter() {

View File

@@ -5,8 +5,8 @@ import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -15,11 +15,10 @@ interface TransportKeyManager {
void start(Transaction txn) throws DbException; void start(Transaction txn) throws DbException;
TransportKeySetId addContact(Transaction txn, ContactId c, KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
SecretKey rootKey, long timestamp, boolean alice, boolean active) long timestamp, boolean alice, boolean active) throws DbException;
throws DbException;
void activateKeys(Transaction txn, TransportKeySetId k) throws DbException; void activateKeys(Transaction txn, KeySetId k) throws DbException;
void removeContact(ContactId c); void removeContact(ContactId c);

View File

@@ -11,9 +11,9 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.system.Clock; import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.Scheduler; import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.transport.ReorderingWindow.Change; import org.briarproject.bramble.transport.ReorderingWindow.Change;
@@ -51,12 +51,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Clock clock; private final Clock clock;
private final TransportId transportId; private final TransportId transportId;
private final long timePeriodLength; private final long rotationPeriodLength;
private final AtomicBoolean used = new AtomicBoolean(false); private final AtomicBoolean used = new AtomicBoolean(false);
private final ReentrantLock lock = new ReentrantLock(); private final ReentrantLock lock = new ReentrantLock();
// The following are locking: lock // The following are locking: lock
private final Map<TransportKeySetId, MutableKeySet> keys = new HashMap<>(); private final Map<KeySetId, MutableKeySet> keys = new HashMap<>();
private final Map<Bytes, TagContext> inContexts = new HashMap<>(); private final Map<Bytes, TagContext> inContexts = new HashMap<>();
private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>(); private final Map<ContactId, MutableKeySet> outContexts = new HashMap<>();
@@ -70,7 +70,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
this.scheduler = scheduler; this.scheduler = scheduler;
this.clock = clock; this.clock = clock;
this.transportId = transportId; this.transportId = transportId;
timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE; rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
} }
@Override @Override
@@ -80,9 +80,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
lock.lock(); lock.lock();
try { try {
// Load the transport keys from the DB // Load the transport keys from the DB
Collection<TransportKeySet> loaded = Collection<KeySet> loaded = db.getTransportKeys(txn, transportId);
db.getTransportKeys(txn, transportId); // Rotate the keys to the current rotation period
// Rotate the keys to the current time period
RotationResult rotationResult = rotateKeys(loaded, now); RotationResult rotationResult = rotateKeys(loaded, now);
// Initialise mutable state for all contacts // Initialise mutable state for all contacts
addKeys(rotationResult.current); addKeys(rotationResult.current);
@@ -96,17 +95,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
scheduleKeyRotation(now); scheduleKeyRotation(now);
} }
private RotationResult rotateKeys(Collection<TransportKeySet> keys, private RotationResult rotateKeys(Collection<KeySet> keys, long now) {
long now) {
RotationResult rotationResult = new RotationResult(); RotationResult rotationResult = new RotationResult();
long timePeriod = now / timePeriodLength; long rotationPeriod = now / rotationPeriodLength;
for (TransportKeySet ks : keys) { for (KeySet ks : keys) {
TransportKeys k = ks.getKeys(); TransportKeys k = ks.getTransportKeys();
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, TransportKeys k1 =
timePeriod); transportCrypto.rotateTransportKeys(k, rotationPeriod);
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(), KeySet ks1 = new KeySet(ks.getKeySetId(), ks.getContactId(), k1);
ks.getContactId(), k1); if (k1.getRotationPeriod() > k.getRotationPeriod())
if (k1.getTimePeriod() > k.getTimePeriod())
rotationResult.rotated.add(ks1); rotationResult.rotated.add(ks1);
rotationResult.current.add(ks1); rotationResult.current.add(ks1);
} }
@@ -114,15 +111,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Locking: lock // Locking: lock
private void addKeys(Collection<TransportKeySet> keys) { private void addKeys(Collection<KeySet> keys) {
for (TransportKeySet ks : keys) { for (KeySet ks : keys) {
addKeys(ks.getKeySetId(), ks.getContactId(), addKeys(ks.getKeySetId(), ks.getContactId(),
new MutableTransportKeys(ks.getKeys())); new MutableTransportKeys(ks.getTransportKeys()));
} }
} }
// Locking: lock // Locking: lock
private void addKeys(TransportKeySetId keySetId, ContactId contactId, private void addKeys(KeySetId keySetId, ContactId contactId,
MutableTransportKeys m) { MutableTransportKeys m) {
MutableKeySet ks = new MutableKeySet(keySetId, contactId, m); MutableKeySet ks = new MutableKeySet(keySetId, contactId, m);
keys.put(keySetId, ks); keys.put(keySetId, ks);
@@ -133,7 +130,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Locking: lock // Locking: lock
private void encodeTags(TransportKeySetId keySetId, ContactId contactId, private void encodeTags(KeySetId keySetId, ContactId contactId,
MutableIncomingKeys inKeys) { MutableIncomingKeys inKeys) {
for (long streamNumber : inKeys.getWindow().getUnseen()) { for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = TagContext tagCtx =
@@ -158,7 +155,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
private void scheduleKeyRotation(long now) { private void scheduleKeyRotation(long now) {
long delay = timePeriodLength - now % timePeriodLength; long delay = rotationPeriodLength - now % rotationPeriodLength;
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS); scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
} }
@@ -173,21 +170,20 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public TransportKeySetId addContact(Transaction txn, ContactId c, public KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
SecretKey rootKey, long timestamp, boolean alice, boolean active) long timestamp, boolean alice, boolean active) throws DbException {
throws DbException {
lock.lock(); lock.lock();
try { try {
// Work out what time period the timestamp belongs to // Work out what rotation period the timestamp belongs to
long timePeriod = timestamp / timePeriodLength; long rotationPeriod = timestamp / rotationPeriodLength;
// Derive the transport keys // Derive the transport keys
TransportKeys k = transportCrypto.deriveTransportKeys(transportId, TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, timePeriod, alice, active); master, rotationPeriod, alice, active);
// Rotate the keys to the current time period if necessary // Rotate the keys to the current rotation period if necessary
timePeriod = clock.currentTimeMillis() / timePeriodLength; rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
k = transportCrypto.rotateTransportKeys(k, timePeriod); k = transportCrypto.rotateTransportKeys(k, rotationPeriod);
// Write the keys back to the DB // Write the keys back to the DB
TransportKeySetId keySetId = db.addTransportKeys(txn, c, k); KeySetId keySetId = db.addTransportKeys(txn, c, k);
// Initialise mutable state for the contact // Initialise mutable state for the contact
addKeys(keySetId, c, new MutableTransportKeys(k)); addKeys(keySetId, c, new MutableTransportKeys(k));
return keySetId; return keySetId;
@@ -197,8 +193,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
@Override @Override
public void activateKeys(Transaction txn, TransportKeySetId k) public void activateKeys(Transaction txn, KeySetId k) throws DbException {
throws DbException {
lock.lock(); lock.lock();
try { try {
MutableKeySet ks = keys.get(k); MutableKeySet ks = keys.get(k);
@@ -305,7 +300,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
} }
// Write the window back to the DB // Write the window back to the DB
db.setReorderingWindow(txn, tagCtx.keySetId, transportId, db.setReorderingWindow(txn, tagCtx.keySetId, transportId,
inKeys.getTimePeriod(), window.getBase(), inKeys.getRotationPeriod(), window.getBase(),
window.getBitmap()); window.getBitmap());
// If the outgoing keys are inactive, activate them // If the outgoing keys are inactive, activate them
MutableKeySet ks = keys.get(tagCtx.keySetId); MutableKeySet ks = keys.get(tagCtx.keySetId);
@@ -327,11 +322,11 @@ class TransportKeyManagerImpl implements TransportKeyManager {
long now = clock.currentTimeMillis(); long now = clock.currentTimeMillis();
lock.lock(); lock.lock();
try { try {
// Rotate the keys to the current time period // Rotate the keys to the current rotation period
Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size()); Collection<KeySet> snapshot = new ArrayList<>(keys.size());
for (MutableKeySet ks : keys.values()) { for (MutableKeySet ks : keys.values()) {
snapshot.add(new TransportKeySet(ks.getKeySetId(), snapshot.add(new KeySet(ks.getKeySetId(), ks.getContactId(),
ks.getContactId(), ks.getTransportKeys().snapshot())); ks.getTransportKeys().snapshot()));
} }
RotationResult rotationResult = rotateKeys(snapshot, now); RotationResult rotationResult = rotateKeys(snapshot, now);
// Rebuild the mutable state for all contacts // Rebuild the mutable state for all contacts
@@ -351,12 +346,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private static class TagContext { private static class TagContext {
private final TransportKeySetId keySetId; private final KeySetId keySetId;
private final ContactId contactId; private final ContactId contactId;
private final MutableIncomingKeys inKeys; private final MutableIncomingKeys inKeys;
private final long streamNumber; private final long streamNumber;
private TagContext(TransportKeySetId keySetId, ContactId contactId, private TagContext(KeySetId keySetId, ContactId contactId,
MutableIncomingKeys inKeys, long streamNumber) { MutableIncomingKeys inKeys, long streamNumber) {
this.keySetId = keySetId; this.keySetId = keySetId;
this.contactId = contactId; this.contactId = contactId;
@@ -367,7 +362,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
private static class RotationResult { private static class RotationResult {
private final Collection<TransportKeySet> current = new ArrayList<>(); private final Collection<KeySet> current = new ArrayList<>();
private final Collection<TransportKeySet> rotated = new ArrayList<>(); private final Collection<KeySet> rotated = new ArrayList<>();
} }
} }

View File

@@ -12,8 +12,6 @@ import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata; import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException; import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -60,7 +58,6 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
private final DatabaseComponent db; private final DatabaseComponent db;
private final ClientHelper clientHelper; private final ClientHelper clientHelper;
private final IdentityManager identityManager;
private final ContactGroupFactory contactGroupFactory; private final ContactGroupFactory contactGroupFactory;
private final Clock clock; private final Clock clock;
private final Group localGroup; private final Group localGroup;
@@ -71,11 +68,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
@Inject @Inject
ClientVersioningManagerImpl(DatabaseComponent db, ClientHelper clientHelper, ClientVersioningManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
IdentityManager identityManager,
ContactGroupFactory contactGroupFactory, Clock clock) { ContactGroupFactory contactGroupFactory, Clock clock) {
this.db = db; this.db = db;
this.clientHelper = clientHelper; this.clientHelper = clientHelper;
this.identityManager = identityManager;
this.contactGroupFactory = contactGroupFactory; this.contactGroupFactory = contactGroupFactory;
this.clock = clock; this.clock = clock;
localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID, localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
@@ -94,9 +89,14 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
public Visibility getClientVisibility(Transaction txn, ContactId contactId, public Visibility getClientVisibility(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException { ClientId clientId, int majorVersion) throws DbException {
try { try {
LatestUpdates latest = findLatestUpdates(txn, contactId); Contact contact = db.getContact(txn, contactId);
if (latest == null || latest.remote == null) return INVISIBLE; Group g = getContactGroup(contact);
// Contact may be in the process of being added or removed, so
// contact group may not exist
if (!db.containsGroup(txn, g.getId())) return INVISIBLE;
LatestUpdates latest = findLatestUpdates(txn, g.getId());
if (latest.local == null) throw new DbException(); if (latest.local == null) throw new DbException();
if (latest.remote == null) return INVISIBLE;
Update localUpdate = loadUpdate(txn, latest.local.messageId); Update localUpdate = loadUpdate(txn, latest.local.messageId);
Update remoteUpdate = loadUpdate(txn, latest.remote.messageId); Update remoteUpdate = loadUpdate(txn, latest.remote.messageId);
Map<ClientMajorVersion, Visibility> visibilities = Map<ClientMajorVersion, Visibility> visibilities =
@@ -110,24 +110,6 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
} }
} }
@Override
public int getClientMinorVersion(Transaction txn, ContactId contactId,
ClientId clientId, int majorVersion) throws DbException {
try {
LatestUpdates latest = findLatestUpdates(txn, contactId);
if (latest == null || latest.remote == null) return -1;
Update remoteUpdate = loadUpdate(txn, latest.remote.messageId);
ClientMajorVersion cv =
new ClientMajorVersion(clientId, majorVersion);
for (ClientState remote : remoteUpdate.states) {
if (remote.majorVersion.equals(cv)) return remote.minorVersion;
}
return -1;
} catch (FormatException e) {
throw new DbException(e);
}
}
@Override @Override
public void createLocalState(Transaction txn) throws DbException { public void createLocalState(Transaction txn) throws DbException {
if (db.containsGroup(txn, localGroup.getId())) return; if (db.containsGroup(txn, localGroup.getId())) return;
@@ -159,7 +141,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
@Override @Override
public void addingContact(Transaction txn, Contact c) throws DbException { public void addingContact(Transaction txn, Contact c) throws DbException {
// Create a group and share it with the contact // Create a group and share it with the contact
Group g = getContactGroup(c, getLocalAuthorId(txn)); Group g = getContactGroup(c);
db.addGroup(txn, g); db.addGroup(txn, g);
db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED); db.setGroupVisibility(txn, c.getId(), g.getId(), SHARED);
// Attach the contact ID to the group // Attach the contact ID to the group
@@ -178,7 +160,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
@Override @Override
public void removingContact(Transaction txn, Contact c) throws DbException { public void removingContact(Transaction txn, Contact c) throws DbException {
db.removeGroup(txn, getContactGroup(c, getLocalAuthorId(txn))); db.removeGroup(txn, getContactGroup(c));
} }
@Override @Override
@@ -313,7 +295,7 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
List<ClientVersion> versions) throws DbException { List<ClientVersion> versions) throws DbException {
try { try {
// Find the latest local and remote updates // Find the latest local and remote updates
Group g = getContactGroup(c, getLocalAuthorId(txn)); Group g = getContactGroup(c);
LatestUpdates latest = findLatestUpdates(txn, g.getId()); LatestUpdates latest = findLatestUpdates(txn, g.getId());
// Load and parse the latest local update // Load and parse the latest local update
if (latest.local == null) throw new DbException(); if (latest.local == null) throw new DbException();
@@ -349,24 +331,9 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
} }
} }
private AuthorId getLocalAuthorId(Transaction txn) throws DbException { private Group getContactGroup(Contact c) {
return identityManager.getLocalAuthor(txn).getId();
}
private Group getContactGroup(Contact c, AuthorId local) {
return contactGroupFactory.createContactGroup(CLIENT_ID, return contactGroupFactory.createContactGroup(CLIENT_ID,
MAJOR_VERSION, c, local); MAJOR_VERSION, c);
}
@Nullable
private LatestUpdates findLatestUpdates(Transaction txn, ContactId c)
throws DbException, FormatException {
Contact contact = db.getContact(txn, c);
Group g = getContactGroup(contact, getLocalAuthorId(txn));
// Contact may be in the process of being added or removed, so
// contact group may not exist
if (!db.containsGroup(txn, g.getId())) return null;
return findLatestUpdates(txn, g.getId());
} }
private LatestUpdates findLatestUpdates(Transaction txn, GroupId g) private LatestUpdates findLatestUpdates(Transaction txn, GroupId g)

View File

@@ -2,4 +2,3 @@ Bridge obfs4 78.46.188.239:37356 5A2D2F4158D0453E00C7C176978D3F41D69C45DB cert=3
Bridge obfs4 52.15.78.72:9443 02069A3C5362476936B62BA6F5ACC41ABD573A9B cert=ijYG/OKc7kqu2YzKNFfeXN7/BG2BOgfEP2KyYEiGDQthnHbsOiTWHeIG0WJVW+BckzDgKw iat-mode=0 Bridge obfs4 52.15.78.72:9443 02069A3C5362476936B62BA6F5ACC41ABD573A9B cert=ijYG/OKc7kqu2YzKNFfeXN7/BG2BOgfEP2KyYEiGDQthnHbsOiTWHeIG0WJVW+BckzDgKw iat-mode=0
Bridge obfs4 13.58.29.242:9443 0C58939A77DA6B6B29D4B5236A75865659607AE0 cert=OylWIEHb/ezpq1zWxW0sgKRn+9ARH2eOcQOZ8/Gew+4l+oKOhQ2jUX/Y+FSl61JorXZUWA iat-mode=0 Bridge obfs4 13.58.29.242:9443 0C58939A77DA6B6B29D4B5236A75865659607AE0 cert=OylWIEHb/ezpq1zWxW0sgKRn+9ARH2eOcQOZ8/Gew+4l+oKOhQ2jUX/Y+FSl61JorXZUWA iat-mode=0
Bridge obfs4 45.33.37.112:9443 60A609BB4ABE8D46E634AE81ED29ADAB7776B399 cert=t5v19WmNv5Sc2YPNr8RQids365W7MY8zJwQVkOxBjUMFomMWARDzsbYpcWLLcw0J9Gm+BQ iat-mode=0 Bridge obfs4 45.33.37.112:9443 60A609BB4ABE8D46E634AE81ED29ADAB7776B399 cert=t5v19WmNv5Sc2YPNr8RQids365W7MY8zJwQVkOxBjUMFomMWARDzsbYpcWLLcw0J9Gm+BQ iat-mode=0
Bridge meek_lite 0.0.2.0:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com

View File

@@ -5,20 +5,27 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.Transaction; import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.AuthorInfo; import org.briarproject.bramble.api.identity.AuthorInfo;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.DbExpectations; import org.briarproject.bramble.test.DbExpectations;
import org.jmock.Expectations;
import org.jmock.Mockery; import org.jmock.Mockery;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections;
import java.util.Random; import java.util.Random;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList; import static java.util.Collections.singletonList;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.OURSELVES;
@@ -26,8 +33,8 @@ import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED; import static org.briarproject.bramble.api.identity.AuthorInfo.Status.VERIFIED;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.util.StringUtils.getRandomString; import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -42,40 +49,42 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
private final IdentityManager identityManager = private final IdentityManager identityManager =
context.mock(IdentityManager.class); context.mock(IdentityManager.class);
private final ContactManager contactManager; private final ContactManager contactManager;
private final Author author = getAuthor(); private final ContactId contactId = new ContactId(42);
private final Author remote = getAuthor();
private final AuthorId local = new AuthorId(getRandomId());
private final LocalAuthor localAuthor = getLocalAuthor(); private final LocalAuthor localAuthor = getLocalAuthor();
private final String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
private final boolean verified = false, active = true; private final boolean verified = false, active = true;
private final Contact contact = getContact(author, verified); private final Contact contact =
private final ContactId contactId = contact.getId(); new Contact(contactId, remote, local, alias, verified, active);
public ContactManagerImplTest() { public ContactManagerImplTest() {
contactManager = contactManager = new ContactManagerImpl(db, keyManager, identityManager);
new ContactManagerImpl(db, keyManager, identityManager);
} }
@Test @Test
public void testAddContact() throws Exception { public void testAddContact() throws Exception {
SecretKey rootKey = getSecretKey(); SecretKey master = getSecretKey();
long timestamp = System.currentTimeMillis(); long timestamp = System.currentTimeMillis();
boolean alice = new Random().nextBoolean(); boolean alice = new Random().nextBoolean();
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(false), withDbCallable(txn)); oneOf(db).transactionWithResult(with(false), withDbCallable(txn));
oneOf(db).addContact(txn, author, verified); oneOf(db).addContact(txn, remote, local, verified, active);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(keyManager).addContact(txn, contactId, rootKey, timestamp, oneOf(keyManager).addContact(txn, contactId, master, timestamp,
alice, active); alice, active);
oneOf(db).getContact(txn, contactId); oneOf(db).getContact(txn, contactId);
will(returnValue(contact)); will(returnValue(contact));
}}); }});
assertEquals(contactId, contactManager.addContact(author, rootKey, assertEquals(contactId, contactManager.addContact(remote, local,
timestamp, alice, verified, active)); master, timestamp, alice, verified, active));
} }
@Test @Test
public void testGetContactByContactId() throws Exception { public void testGetContact() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
@@ -87,20 +96,49 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
} }
@Test @Test
public void testGetContactByAuthorId() throws Exception { public void testGetContactByAuthor() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}});
assertEquals(contact, contactManager.getContact(remote.getId(), local));
}
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownAuthor() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContact(txn, author.getId()); oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contact)); will(returnValue(emptyList()));
}}); }});
assertEquals(contact, contactManager.getContact(author.getId())); contactManager.getContact(remote.getId(), local);
}
@Test(expected = NoSuchContactException.class)
public void testGetContactByUnknownLocalAuthor() throws Exception {
Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = Collections.singleton(contact);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}});
contactManager.getContact(remote.getId(), new AuthorId(getRandomId()));
} }
@Test @Test
public void testGetContacts() throws Exception { public void testGetActiveContacts() throws Exception {
Collection<Contact> contacts = singletonList(contact); Collection<Contact> activeContacts = Collections.singletonList(contact);
Collection<Contact> contacts = new ArrayList<>(activeContacts);
contacts.add(new Contact(new ContactId(3), remote, local, alias, true,
false));
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
@@ -108,7 +146,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
will(returnValue(contacts)); will(returnValue(contacts));
}}); }});
assertEquals(contacts, contactManager.getContacts()); assertEquals(activeContacts, contactManager.getActiveContacts());
} }
@Test @Test
@@ -124,11 +162,19 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
contactManager.removeContact(contactId); contactManager.removeContact(contactId);
} }
@Test
public void testSetContactActive() throws Exception {
Transaction txn = new Transaction(null, false);
context.checking(new Expectations() {{
oneOf(db).setContactActive(txn, contactId, active);
}});
contactManager.setContactActive(txn, contactId, active);
}
@Test @Test
public void testSetContactAlias() throws Exception { public void testSetContactAlias() throws Exception {
Transaction txn = new Transaction(null, false); Transaction txn = new Transaction(null, false);
String alias = getRandomString(MAX_AUTHOR_NAME_LENGTH);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transaction(with(false), withDbRunnable(txn)); oneOf(db).transaction(with(false), withDbRunnable(txn));
oneOf(db).setContactAlias(txn, contactId, alias); oneOf(db).setContactAlias(txn, contactId, alias);
@@ -149,82 +195,85 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(db).containsContact(txn, author.getId()); oneOf(db).containsContact(txn, remote.getId(), local);
will(returnValue(true)); will(returnValue(true));
}}); }});
assertTrue(contactManager.contactExists(author.getId())); assertTrue(contactManager.contactExists(remote.getId(), local));
} }
@Test @Test
public void testGetAuthorInfoOurselves() throws Exception { public void testGetAuthorInfo() throws Exception {
Transaction txn = new Transaction(null, true); Transaction txn = new Transaction(null, true);
Collection<Contact> contacts = singletonList(
new Contact(new ContactId(1), remote, localAuthor.getId(),
alias, false, true));
context.checking(new DbExpectations() {{ context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn)); oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn); oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor)); will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(contacts));
}}); }});
AuthorInfo authorInfo = AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, localAuthor.getId()); contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
}
@Test
public void testGetAuthorInfoTransaction() throws DbException {
Transaction txn = new Transaction(null, true);
// check unknown author
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).getContactsByAuthorId(txn, remote.getId());
will(returnValue(emptyList()));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNKNOWN, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
// check unverified contact
Collection<Contact> contacts = singletonList(
new Contact(new ContactId(1), remote, localAuthor.getId(),
alias, false, true));
checkAuthorInfoContext(txn, remote.getId(), contacts);
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
// check verified contact
contacts = singletonList(new Contact(new ContactId(1), remote,
localAuthor.getId(), alias, true, true));
checkAuthorInfoContext(txn, remote.getId(), contacts);
authorInfo = contactManager.getAuthorInfo(txn, remote.getId());
assertEquals(VERIFIED, authorInfo.getStatus());
assertEquals(alias, contact.getAlias());
// check ourselves
context.checking(new Expectations() {{
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
never(db).getContactsByAuthorId(txn, remote.getId());
}});
authorInfo = contactManager.getAuthorInfo(txn, localAuthor.getId());
assertEquals(OURSELVES, authorInfo.getStatus()); assertEquals(OURSELVES, authorInfo.getStatus());
assertNull(authorInfo.getAlias()); assertNull(authorInfo.getAlias());
} }
@Test private void checkAuthorInfoContext(Transaction txn, AuthorId authorId,
public void testGetAuthorInfoVerified() throws Exception { Collection<Contact> contacts) throws DbException {
Transaction txn = new Transaction(null, true); context.checking(new Expectations() {{
Contact verified = getContact(author, true);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn); oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor)); will(returnValue(localAuthor));
oneOf(db).containsContact(txn, author.getId()); oneOf(db).getContactsByAuthorId(txn, authorId);
will(returnValue(true)); will(returnValue(contacts));
oneOf(db).getContact(txn, author.getId());
will(returnValue(verified));
}}); }});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(VERIFIED, authorInfo.getStatus());
assertEquals(verified.getAlias(), authorInfo.getAlias());
} }
@Test
public void testGetAuthorInfoUnverified() throws Exception {
Transaction txn = new Transaction(null, true);
Contact unverified = getContact(author, false);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).containsContact(txn, author.getId());
will(returnValue(true));
oneOf(db).getContact(txn, author.getId());
will(returnValue(unverified));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(UNVERIFIED, authorInfo.getStatus());
assertEquals(unverified.getAlias(), authorInfo.getAlias());
}
@Test
public void testGetAuthorInfoUnknown() throws Exception {
Transaction txn = new Transaction(null, true);
context.checking(new DbExpectations() {{
oneOf(db).transactionWithResult(with(true), withDbCallable(txn));
oneOf(identityManager).getLocalAuthor(txn);
will(returnValue(localAuthor));
oneOf(db).containsContact(txn, author.getId());
will(returnValue(false));
}});
AuthorInfo authorInfo =
contactManager.getAuthorInfo(txn, author.getId());
assertEquals(UNKNOWN, authorInfo.getStatus());
assertNull(authorInfo.getAlias());
}
} }

View File

@@ -1,167 +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.HandshakeKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test;
import java.util.Arrays;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
public class HandshakeKeyDerivationTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final TransportId transportId = getTransportId();
private final SecretKey rootKey = getSecretKey();
@Test
public void testKeysAreDistinct() {
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
assertAllDifferent(kA);
assertAllDifferent(kB);
}
@Test
public void testKeysAreNotUpdatedToPreviousPeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 122);
assertSame(k, k1);
}
@Test
public void testKeysAreNotUpdatedToCurrentPeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 123);
assertSame(k, k1);
}
@Test
public void testKeysAreUpdatedByOnePeriod() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
}
@Test
public void testKeysAreUpdatedByTwoPeriods() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
}
@Test
public void testKeysAreUpdatedByThreePeriods() {
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.updateHandshakeKeys(k, 126);
assertAllDifferent(k, k1);
}
@Test
public void testCurrentKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
// Update into the future
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
}
@Test
public void testPreviousKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's current
// outgoing keys
assertMatches(kA.getPreviousIncomingKeys(),
kB.getCurrentOutgoingKeys());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.updateHandshakeKeys(kB, 457);
// Bob's previous incoming keys should equal Alice's current
// outgoing keys
assertMatches(kB.getPreviousIncomingKeys(),
kA.getCurrentOutgoingKeys());
}
@Test
public void testNextKeysMatchContact() {
// Start in time period 123
HandshakeKeys kA = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys kB = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, false);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = transportCrypto.updateHandshakeKeys(kA, 456);
kB = transportCrypto.updateHandshakeKeys(kB, 455);
// Bob's next incoming keys should equal Alice's current outgoing keys
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.updateHandshakeKeys(kB, 457);
// Alice's next incoming keys should equal Bob's current outgoing keys
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
}
@Test
public void testRootKeyAffectsOutput() {
SecretKey rootKey1 = getSecretKey();
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.deriveHandshakeKeys(transportId,
rootKey1, 123, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString());
HandshakeKeys k = transportCrypto.deriveHandshakeKeys(transportId,
rootKey, 123, true);
HandshakeKeys k1 = transportCrypto.deriveHandshakeKeys(transportId1,
rootKey, 123, true);
assertAllDifferent(k, k1);
}
}

View File

@@ -0,0 +1,167 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
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.TransportKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class KeyDerivationTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final TransportId transportId = getTransportId();
private final SecretKey master = getSecretKey();
@Test
public void testKeysAreDistinct() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
assertAllDifferent(k);
}
@Test
public void testCurrentKeysMatchCurrentKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false, true);
// Alice's incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Alice's outgoing keys should equal Bob's incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getCurrentIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
// Rotate into the future
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getCurrentIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Alice's outgoing keys should equal Bob's incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getCurrentIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getCurrentIncomingKeys().getHeaderKey().getBytes());
}
@Test
public void testPreviousKeysMatchPreviousKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false, true);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getPreviousIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(
kA.getPreviousIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's outgoing keys should equal Bob's previous incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getPreviousIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getPreviousIncomingKeys().getHeaderKey().getBytes());
}
@Test
public void testNextKeysMatchNextKeysOfContact() {
// Start in rotation period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
master, 123, false, true);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's outgoing keys should equal Bob's next incoming keys
assertArrayEquals(kA.getCurrentOutgoingKeys().getTagKey().getBytes(),
kB.getNextIncomingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getCurrentOutgoingKeys().getHeaderKey().getBytes(),
kB.getNextIncomingKeys().getHeaderKey().getBytes());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's outgoing keys
assertArrayEquals(kA.getNextIncomingKeys().getTagKey().getBytes(),
kB.getCurrentOutgoingKeys().getTagKey().getBytes());
assertArrayEquals(kA.getNextIncomingKeys().getHeaderKey().getBytes(),
kB.getCurrentOutgoingKeys().getHeaderKey().getBytes());
}
@Test
public void testMasterKeyAffectsOutput() {
SecretKey master1 = getSecretKey();
assertFalse(Arrays.equals(master.getBytes(), master1.getBytes()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
master1, 123, true, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertFalse(transportId.getString().equals(transportId1.getString()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
master, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
master, 123, true, true);
assertAllDifferent(k, k1);
}
private void assertAllDifferent(TransportKeys... transportKeys) {
List<SecretKey> secretKeys = new ArrayList<>();
for (TransportKeys k : transportKeys) {
secretKeys.add(k.getPreviousIncomingKeys().getTagKey());
secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentIncomingKeys().getTagKey());
secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey());
secretKeys.add(k.getNextIncomingKeys().getTagKey());
secretKeys.add(k.getNextIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentOutgoingKeys().getTagKey());
secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey());
}
assertAllDifferent(secretKeys);
}
private void assertAllDifferent(List<SecretKey> keys) {
Set<Bytes> set = new HashSet<>();
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
}
}

View File

@@ -1,45 +0,0 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.transport.AbstractTransportKeys;
import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.OutgoingKeys;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertTrue;
class KeyDerivationTestUtils {
static void assertAllDifferent(AbstractTransportKeys... transportKeys) {
List<SecretKey> secretKeys = new ArrayList<>();
for (AbstractTransportKeys k : transportKeys) {
secretKeys.add(k.getPreviousIncomingKeys().getTagKey());
secretKeys.add(k.getPreviousIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentIncomingKeys().getTagKey());
secretKeys.add(k.getCurrentIncomingKeys().getHeaderKey());
secretKeys.add(k.getNextIncomingKeys().getTagKey());
secretKeys.add(k.getNextIncomingKeys().getHeaderKey());
secretKeys.add(k.getCurrentOutgoingKeys().getTagKey());
secretKeys.add(k.getCurrentOutgoingKeys().getHeaderKey());
}
assertAllDifferent(secretKeys);
}
static void assertAllDifferent(List<SecretKey> keys) {
Set<Bytes> set = new HashSet<>();
for (SecretKey k : keys) assertTrue(set.add(new Bytes(k.getBytes())));
}
static void assertMatches(IncomingKeys in, OutgoingKeys out) {
assertArrayEquals(in.getTagKey().getBytes(),
out.getTagKey().getBytes());
assertArrayEquals(in.getHeaderKey().getBytes(),
out.getHeaderKey().getBytes());
}
}

View File

@@ -1,167 +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.TransportKeys;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.junit.Test;
import java.util.Arrays;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertAllDifferent;
import static org.briarproject.bramble.crypto.KeyDerivationTestUtils.assertMatches;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertSame;
public class TransportKeyDerivationTest extends BrambleTestCase {
private final CryptoComponent crypto =
new CryptoComponentImpl(new TestSecureRandomProvider(), null);
private final TransportCrypto transportCrypto =
new TransportCryptoImpl(crypto);
private final TransportId transportId = getTransportId();
private final SecretKey rootKey = getSecretKey();
@Test
public void testKeysAreDistinct() {
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
assertAllDifferent(kA);
assertAllDifferent(kB);
}
@Test
public void testKeysAreNotRotatedToPreviousPeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 122);
assertSame(k, k1);
}
@Test
public void testKeysAreNotRotatedToCurrentPeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 123);
assertSame(k, k1);
}
@Test
public void testKeysAreRotatedByOnePeriod() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 124);
assertSame(k.getCurrentIncomingKeys(), k1.getPreviousIncomingKeys());
assertSame(k.getNextIncomingKeys(), k1.getCurrentIncomingKeys());
}
@Test
public void testKeysAreRotatedByTwoPeriods() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 125);
assertSame(k.getNextIncomingKeys(), k1.getPreviousIncomingKeys());
}
@Test
public void testKeysAreRotatedByThreePeriods() {
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.rotateTransportKeys(k, 126);
assertAllDifferent(k, k1);
}
@Test
public void testCurrentKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
// Rotate into the future
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 456);
// Alice's incoming keys should equal Bob's outgoing keys
assertMatches(kA.getCurrentIncomingKeys(), kB.getCurrentOutgoingKeys());
// Bob's incoming keys should equal Alice's outgoing keys
assertMatches(kB.getCurrentIncomingKeys(), kA.getCurrentOutgoingKeys());
}
@Test
public void testPreviousKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// Compare Alice's previous keys in period 456 with Bob's current keys
// in period 455
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Alice's previous incoming keys should equal Bob's current
// outgoing keys
assertMatches(kA.getPreviousIncomingKeys(),
kB.getCurrentOutgoingKeys());
// Compare Alice's current keys in period 456 with Bob's previous keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Bob's previous incoming keys should equal Alice's current
// outgoing keys
assertMatches(kB.getPreviousIncomingKeys(),
kA.getCurrentOutgoingKeys());
}
@Test
public void testNextKeysMatchContact() {
// Start in time period 123
TransportKeys kA = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys kB = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, false, true);
// Compare Alice's current keys in period 456 with Bob's next keys in
// period 455
kA = transportCrypto.rotateTransportKeys(kA, 456);
kB = transportCrypto.rotateTransportKeys(kB, 455);
// Bob's next incoming keys should equal Alice's current outgoing keys
assertMatches(kB.getNextIncomingKeys(), kA.getCurrentOutgoingKeys());
// Compare Alice's next keys in period 456 with Bob's current keys
// in period 457
kB = transportCrypto.rotateTransportKeys(kB, 457);
// Alice's next incoming keys should equal Bob's current outgoing keys
assertMatches(kA.getNextIncomingKeys(), kB.getCurrentOutgoingKeys());
}
@Test
public void testRootKeyAffectsOutput() {
SecretKey rootKey1 = getSecretKey();
assertFalse(Arrays.equals(rootKey.getBytes(), rootKey1.getBytes()));
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId,
rootKey1, 123, true, true);
assertAllDifferent(k, k1);
}
@Test
public void testTransportIdAffectsOutput() {
TransportId transportId1 = getTransportId();
assertNotEquals(transportId.getString(), transportId1.getString());
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
rootKey, 123, true, true);
TransportKeys k1 = transportCrypto.deriveTransportKeys(transportId1,
rootKey, 123, true, true);
assertAllDifferent(k, k1);
}
}

View File

@@ -2,9 +2,9 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent; import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent; import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent; import org.briarproject.bramble.api.db.DatabaseComponent;
@@ -13,9 +13,7 @@ import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException; import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException; import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
import org.briarproject.bramble.api.db.NoSuchMessageException; import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.db.NoSuchPendingContactException;
import org.briarproject.bramble.api.db.NoSuchTransportException; import org.briarproject.bramble.api.db.NoSuchTransportException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
@@ -45,21 +43,18 @@ import org.briarproject.bramble.api.sync.event.MessageToAckEvent;
import org.briarproject.bramble.api.sync.event.MessageToRequestEvent; import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent; import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent; import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.transport.HandshakeKeys;
import org.briarproject.bramble.api.transport.IncomingKeys; import org.briarproject.bramble.api.transport.IncomingKeys;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.OutgoingKeys; import org.briarproject.bramble.api.transport.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction; import org.briarproject.bramble.test.CaptureArgumentAction;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.Sequence;
import org.junit.Test; import org.junit.Test;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference; import java.util.concurrent.atomic.AtomicReference;
import static java.util.Arrays.asList; import static java.util.Arrays.asList;
@@ -75,13 +70,13 @@ import static org.briarproject.bramble.api.transport.TransportConstants.REORDERI
import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES; import static org.briarproject.bramble.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
import static org.briarproject.bramble.test.TestUtils.getAuthor; import static org.briarproject.bramble.test.TestUtils.getAuthor;
import static org.briarproject.bramble.test.TestUtils.getClientId; import static org.briarproject.bramble.test.TestUtils.getClientId;
import static org.briarproject.bramble.test.TestUtils.getContact;
import static org.briarproject.bramble.test.TestUtils.getGroup; import static org.briarproject.bramble.test.TestUtils.getGroup;
import static org.briarproject.bramble.test.TestUtils.getLocalAuthor; import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getMessage; import static org.briarproject.bramble.test.TestUtils.getMessage;
import static org.briarproject.bramble.test.TestUtils.getRandomId; import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey; import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTransportId; import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@@ -92,10 +87,9 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private final Database<Object> database = context.mock(Database.class); private final Database<Object> database = context.mock(Database.class);
private final ShutdownManager shutdownManager = private final ShutdownManager shutdown =
context.mock(ShutdownManager.class); context.mock(ShutdownManager.class);
private final EventBus eventBus = context.mock(EventBus.class); private final EventBus eventBus = context.mock(EventBus.class);
private final Executor eventExecutor = context.mock(Executor.class);
private final SecretKey key = getSecretKey(); private final SecretKey key = getSecretKey();
private final Object txn = new Object(); private final Object txn = new Object();
@@ -113,8 +107,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
private final int maxLatency; private final int maxLatency;
private final ContactId contactId; private final ContactId contactId;
private final Contact contact; private final Contact contact;
private final TransportKeySetId keySetId; private final KeySetId keySetId;
private final PendingContactId pendingContactId;
public DatabaseComponentImplTest() { public DatabaseComponentImplTest() {
clientId = getClientId(); clientId = getClientId();
@@ -123,6 +116,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
groupId = group.getId(); groupId = group.getId();
author = getAuthor(); author = getAuthor();
localAuthor = getLocalAuthor(); localAuthor = getLocalAuthor();
alias = getRandomString(5);
message = getMessage(groupId); message = getMessage(groupId);
message1 = getMessage(groupId); message1 = getMessage(groupId);
messageId = message.getId(); messageId = message.getId();
@@ -131,18 +125,16 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
metadata.put("foo", new byte[] {'b', 'a', 'r'}); metadata.put("foo", new byte[] {'b', 'a', 'r'});
transportId = getTransportId(); transportId = getTransportId();
maxLatency = Integer.MAX_VALUE; maxLatency = Integer.MAX_VALUE;
contact = getContact(author, true); contactId = new ContactId(234);
contactId = contact.getId(); contact = new Contact(contactId, author, localAuthor.getId(), alias,
alias = contact.getAlias(); true, true);
keySetId = new TransportKeySetId(345); keySetId = new KeySetId(345);
pendingContactId = new PendingContactId(getRandomId());
} }
private DatabaseComponent createDatabaseComponent(Database<Object> database, private DatabaseComponent createDatabaseComponent(Database<Object> database,
EventBus eventBus, Executor eventExecutor, EventBus eventBus, ShutdownManager shutdown) {
ShutdownManager shutdownManager) {
return new DatabaseComponentImpl<>(database, Object.class, eventBus, return new DatabaseComponentImpl<>(database, Object.class, eventBus,
eventExecutor, shutdownManager); shutdown);
} }
@Test @Test
@@ -152,7 +144,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// open() // open()
oneOf(database).open(key, null); oneOf(database).open(key, null);
will(returnValue(false)); will(returnValue(false));
oneOf(shutdownManager).addShutdownHook(with(any(Runnable.class))); oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle)); will(returnValue(shutdownHandle));
// startTransaction() // startTransaction()
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -163,13 +155,19 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).addLocalAuthor(txn, localAuthor); oneOf(database).addLocalAuthor(txn, localAuthor);
oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class))); oneOf(eventBus).broadcast(with(any(LocalAuthorAddedEvent.class)));
// addContact() // addContact()
oneOf(database).containsLocalAuthor(txn, localAuthor.getId());
will(returnValue(true));
oneOf(database).containsLocalAuthor(txn, author.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(database).containsContact(txn, author.getId()); oneOf(database).containsContact(txn, author.getId(),
localAuthor.getId());
will(returnValue(false)); will(returnValue(false));
oneOf(database).addContact(txn, author, true); oneOf(database).addContact(txn, author, localAuthor.getId(),
true, true);
will(returnValue(contactId)); will(returnValue(contactId));
oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class))); oneOf(eventBus).broadcast(with(any(ContactAddedEvent.class)));
oneOf(eventBus).broadcast(with(any(
ContactStatusChangedEvent.class)));
// getContacts() // getContacts()
oneOf(database).getContacts(txn); oneOf(database).getContacts(txn);
will(returnValue(singletonList(contact))); will(returnValue(singletonList(contact)));
@@ -209,12 +207,13 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).close(); oneOf(database).close();
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
assertFalse(db.open(key, null)); assertFalse(db.open(key, null));
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
db.addLocalAuthor(transaction, localAuthor); db.addLocalAuthor(transaction, localAuthor);
assertEquals(contactId, db.addContact(transaction, author, true)); assertEquals(contactId, db.addContact(transaction, author,
localAuthor.getId(), true, true));
assertEquals(singletonList(contact), assertEquals(singletonList(contact),
db.getContacts(transaction)); db.getContacts(transaction));
db.addGroup(transaction, group); // First time - listeners called db.addGroup(transaction, group); // First time - listeners called
@@ -239,7 +238,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).abortTransaction(txn); oneOf(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addLocalMessage(transaction, message, metadata, true)); db.addLocalMessage(transaction, message, metadata, true));
@@ -264,7 +263,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class))); oneOf(eventBus).broadcast(with(any(MessageSharedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addLocalMessage(transaction, message, metadata, true)); db.addLocalMessage(transaction, message, metadata, true));
@@ -275,25 +274,14 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the contact is in the DB (which it's not) // Check whether the contact is in the DB (which it's not)
exactly(18).of(database).startTransaction(); exactly(17).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(17).of(database).containsContact(txn, contactId); exactly(17).of(database).containsContact(txn, contactId);
will(returnValue(false)); will(returnValue(false));
oneOf(database).containsContact(txn, author.getId()); exactly(17).of(database).abortTransaction(txn);
will(returnValue(false));
exactly(18).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
try {
db.transaction(false, transaction ->
db.addHandshakeKeys(transaction, contactId,
createHandshakeKeys()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -344,14 +332,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.getContact(transaction, author.getId()));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.getMessageStatus(transaction, contactId, groupId)); db.getMessageStatus(transaction, contactId, groupId));
@@ -419,6 +399,14 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// Expected // Expected
} }
try {
db.transaction(false, transaction ->
db.setContactActive(transaction, contactId, true));
fail();
} catch (NoSuchContactException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setContactAlias(transaction, contactId, alias)); db.setContactAlias(transaction, contactId, alias));
@@ -442,15 +430,24 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
throws Exception { throws Exception {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// Check whether the pseudonym is in the DB (which it's not) // Check whether the pseudonym is in the DB (which it's not)
exactly(2).of(database).startTransaction(); exactly(3).of(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
exactly(2).of(database).containsLocalAuthor(txn, exactly(3).of(database).containsLocalAuthor(txn,
localAuthor.getId()); localAuthor.getId());
will(returnValue(false)); will(returnValue(false));
exactly(2).of(database).abortTransaction(txn); exactly(3).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
try {
db.transaction(false, transaction ->
db.addContact(transaction, author, localAuthor.getId(),
true, true));
fail();
} catch (NoSuchLocalAuthorException expected) {
// Expected
}
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -484,7 +481,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -568,7 +565,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
will(returnValue(true)); will(returnValue(true));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -671,7 +668,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
exactly(5).of(database).abortTransaction(txn); exactly(5).of(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -716,39 +713,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
} }
} }
@Test
public void testVariousMethodsThrowExceptionIfPendingContactIsMissing()
throws Exception {
context.checking(new Expectations() {{
// Check whether the pending contact is in the DB (which it's not)
exactly(2).of(database).startTransaction();
will(returnValue(txn));
exactly(2).of(database).containsPendingContact(txn,
pendingContactId);
will(returnValue(false));
exactly(2).of(database).abortTransaction(txn);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
try {
db.transaction(false, transaction ->
db.addHandshakeKeys(transaction, pendingContactId,
createHandshakeKeys()));
fail();
} catch (NoSuchPendingContactException expected) {
// Expected
}
try {
db.transaction(false, transaction ->
db.removePendingContact(transaction, pendingContactId));
fail();
} catch (NoSuchPendingContactException expected) {
// Expected
}
}
@Test @Test
public void testGenerateAck() throws Exception { public void testGenerateAck() throws Exception {
Collection<MessageId> messagesToAck = asList(messageId, messageId1); Collection<MessageId> messagesToAck = asList(messageId, messageId1);
@@ -763,7 +727,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
Ack a = db.generateAck(transaction, contactId, 123); Ack a = db.generateAck(transaction, contactId, 123);
@@ -797,7 +761,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class))); oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
assertEquals(messages, db.generateBatch(transaction, contactId, assertEquals(messages, db.generateBatch(transaction, contactId,
@@ -822,7 +786,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
Offer o = db.generateOffer(transaction, contactId, 123, maxLatency); Offer o = db.generateOffer(transaction, contactId, 123, maxLatency);
@@ -846,7 +810,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
Request r = db.generateRequest(transaction, contactId, 123); Request r = db.generateRequest(transaction, contactId, 123);
@@ -880,7 +844,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class))); oneOf(eventBus).broadcast(with(any(MessagesSentEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
assertEquals(messages, db.generateRequestedBatch(transaction, assertEquals(messages, db.generateRequestedBatch(transaction,
@@ -901,7 +865,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class))); oneOf(eventBus).broadcast(with(any(MessagesAckedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
Ack a = new Ack(singletonList(messageId)); Ack a = new Ack(singletonList(messageId));
@@ -939,7 +903,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class))); oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
// Receive the message twice // Receive the message twice
@@ -967,7 +931,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class))); oneOf(eventBus).broadcast(with(any(MessageToAckEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.receiveMessage(transaction, contactId, message)); db.receiveMessage(transaction, contactId, message));
@@ -985,7 +949,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.receiveMessage(transaction, contactId, message)); db.receiveMessage(transaction, contactId, message));
@@ -1025,7 +989,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageToRequestEvent.class))); oneOf(eventBus).broadcast(with(any(MessageToRequestEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
Offer o = new Offer(asList(messageId, messageId1, Offer o = new Offer(asList(messageId, messageId1,
messageId2, messageId3)); messageId2, messageId3));
@@ -1048,7 +1012,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(eventBus).broadcast(with(any(MessageRequestedEvent.class))); oneOf(eventBus).broadcast(with(any(MessageRequestedEvent.class)));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
Request r = new Request(singletonList(messageId)); Request r = new Request(singletonList(messageId));
db.transaction(false, transaction -> db.transaction(false, transaction ->
@@ -1078,7 +1042,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
GroupVisibilityUpdatedEvent.class, 0)); GroupVisibilityUpdatedEvent.class, 0));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setGroupVisibility(transaction, contactId, groupId, db.setGroupVisibility(transaction, contactId, groupId,
@@ -1112,7 +1076,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
GroupVisibilityUpdatedEvent.class, 0)); GroupVisibilityUpdatedEvent.class, 0));
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setGroupVisibility(transaction, contactId, groupId, db.setGroupVisibility(transaction, contactId, groupId,
@@ -1138,7 +1102,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.setGroupVisibility(transaction, contactId, groupId, db.setGroupVisibility(transaction, contactId, groupId,
@@ -1148,9 +1112,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
@Test @Test
public void testTransportKeys() throws Exception { public void testTransportKeys() throws Exception {
TransportKeys transportKeys = createTransportKeys(); TransportKeys transportKeys = createTransportKeys();
TransportKeySet ks = KeySet ks = new KeySet(keySetId, contactId, transportKeys);
new TransportKeySet(keySetId, contactId, transportKeys); Collection<KeySet> keys = singletonList(ks);
Collection<TransportKeySet> keys = singletonList(ks);
context.checking(new Expectations() {{ context.checking(new Expectations() {{
// startTransaction() // startTransaction()
@@ -1169,7 +1132,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
db.updateTransportKeys(transaction, keys); db.updateTransportKeys(transaction, keys);
@@ -1208,7 +1171,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(true, transaction -> { db.transaction(true, transaction -> {
// With visible group - return stored status // With visible group - return stored status
@@ -1258,7 +1221,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).commitTransaction(txn); oneOf(database).commitTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(true, transaction -> { db.transaction(true, transaction -> {
// With visible group - return stored status // With visible group - return stored status
@@ -1277,27 +1240,6 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}); });
} }
private HandshakeKeys createHandshakeKeys() {
SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey();
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
1, 123, new byte[4]);
SecretKey inCurrTagKey = getSecretKey();
SecretKey inCurrHeaderKey = getSecretKey();
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
2, 234, new byte[4]);
SecretKey inNextTagKey = getSecretKey();
SecretKey inNextHeaderKey = getSecretKey();
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
3, 345, new byte[4]);
SecretKey outCurrTagKey = getSecretKey();
SecretKey outCurrHeaderKey = getSecretKey();
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
2, 456, true);
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
getSecretKey(), true);
}
private TransportKeys createTransportKeys() { private TransportKeys createTransportKeys() {
SecretKey inPrevTagKey = getSecretKey(); SecretKey inPrevTagKey = getSecretKey();
SecretKey inPrevHeaderKey = getSecretKey(); SecretKey inPrevHeaderKey = getSecretKey();
@@ -1345,7 +1287,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
// First merge should broadcast an event // First merge should broadcast an event
@@ -1388,7 +1330,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
assertNotNull(db.startTransaction(firstTxnReadOnly)); assertNotNull(db.startTransaction(firstTxnReadOnly));
db.startTransaction(secondTxnReadOnly); db.startTransaction(secondTxnReadOnly);
@@ -1400,6 +1342,8 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsLocalAuthor(txn, localAuthor.getId());
will(returnValue(true));
// Contact is a local identity // Contact is a local identity
oneOf(database).containsLocalAuthor(txn, author.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(true)); will(returnValue(true));
@@ -1407,11 +1351,12 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, true)); db.addContact(transaction, author, localAuthor.getId(),
true, true));
fail(); fail();
} catch (ContactExistsException expected) { } catch (ContactExistsException expected) {
// Expected // Expected
@@ -1423,20 +1368,24 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
context.checking(new Expectations() {{ context.checking(new Expectations() {{
oneOf(database).startTransaction(); oneOf(database).startTransaction();
will(returnValue(txn)); will(returnValue(txn));
oneOf(database).containsLocalAuthor(txn, localAuthor.getId());
will(returnValue(true));
oneOf(database).containsLocalAuthor(txn, author.getId()); oneOf(database).containsLocalAuthor(txn, author.getId());
will(returnValue(false)); will(returnValue(false));
// Contact already exists // Contact already exists for this local identity
oneOf(database).containsContact(txn, author.getId()); oneOf(database).containsContact(txn, author.getId(),
localAuthor.getId());
will(returnValue(true)); will(returnValue(true));
oneOf(database).abortTransaction(txn); oneOf(database).abortTransaction(txn);
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
try { try {
db.transaction(false, transaction -> db.transaction(false, transaction ->
db.addContact(transaction, author, true)); db.addContact(transaction, author, localAuthor.getId(),
true, true));
fail(); fail();
} catch (ContactExistsException expected) { } catch (ContactExistsException expected) {
// Expected // Expected
@@ -1452,7 +1401,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
// open() // open()
oneOf(database).open(key, null); oneOf(database).open(key, null);
will(returnValue(false)); will(returnValue(false));
oneOf(shutdownManager).addShutdownHook(with(any(Runnable.class))); oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle)); will(returnValue(shutdownHandle));
// startTransaction() // startTransaction()
oneOf(database).startTransaction(); oneOf(database).startTransaction();
@@ -1492,7 +1441,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
oneOf(database).close(); oneOf(database).close();
}}); }});
DatabaseComponent db = createDatabaseComponent(database, eventBus, DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager); shutdown);
assertFalse(db.open(key, null)); assertFalse(db.open(key, null));
db.transaction(false, transaction -> { db.transaction(false, transaction -> {
@@ -1506,43 +1455,4 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
}); });
db.close(); db.close();
} }
@Test
public void testCommitActionsOccurInOrder() throws Exception {
TestEvent action1 = new TestEvent();
Runnable action2 = () -> {
};
TestEvent action3 = new TestEvent();
Runnable action4 = () -> {
};
Sequence sequence = context.sequence("sequence");
context.checking(new Expectations() {{
oneOf(database).startTransaction();
will(returnValue(txn));
inSequence(sequence);
oneOf(database).commitTransaction(txn);
inSequence(sequence);
oneOf(eventBus).broadcast(action1);
inSequence(sequence);
oneOf(eventExecutor).execute(action2);
inSequence(sequence);
oneOf(eventBus).broadcast(action3);
inSequence(sequence);
oneOf(eventExecutor).execute(action4);
inSequence(sequence);
}});
DatabaseComponent db = createDatabaseComponent(database, eventBus,
eventExecutor, shutdownManager);
db.transaction(false, transaction -> {
transaction.attach(action1);
transaction.attach(action2);
transaction.attach(action3);
transaction.attach(action4);
});
}
private static class TestEvent extends Event {
}
} }

View File

@@ -45,7 +45,8 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
private final Migration<Connection> migration1 = private final Migration<Connection> migration1 =
context.mock(Migration.class, "migration1"); context.mock(Migration.class, "migration1");
protected final DatabaseConfig config = new TestDatabaseConfig(testDir); protected final DatabaseConfig config =
new TestDatabaseConfig(testDir, 1024 * 1024);
protected final MessageFactory messageFactory = new TestMessageFactory(); protected final MessageFactory messageFactory = new TestMessageFactory();
protected final SecretKey key = getSecretKey(); protected final SecretKey key = getSecretKey();
protected final Clock clock = new SystemClock(); protected final Clock clock = new SystemClock();

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