mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
241 Commits
block-inpu
...
contact-re
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c290a320e | ||
|
|
421c9c44d6 | ||
|
|
29d3ee2439 | ||
|
|
06d4f85768 | ||
|
|
9685462242 | ||
|
|
84f2c29c76 | ||
|
|
9c8125d77a | ||
|
|
1a1a010ee7 | ||
|
|
56fb20f257 | ||
|
|
f82294527f | ||
|
|
456f25b701 | ||
|
|
0587fdc54c | ||
|
|
ece083026e | ||
|
|
0e5bb3e9de | ||
|
|
dcebd5a81c | ||
|
|
e9a3685bfd | ||
|
|
3aadcc17dd | ||
|
|
296ce080e2 | ||
|
|
724e6643bd | ||
|
|
fafd0c7ff9 | ||
|
|
e91a7c64d8 | ||
|
|
f08e3a58e6 | ||
|
|
94de1834b8 | ||
|
|
6b24eeb84c | ||
|
|
f72ff9f812 | ||
|
|
0f5f440f1c | ||
|
|
7acbe56197 | ||
|
|
fccf735a89 | ||
|
|
d5ac2c9ead | ||
|
|
d4b929fc6c | ||
|
|
b568405f59 | ||
|
|
ff2f710495 | ||
|
|
d00094edab | ||
|
|
9ca854473f | ||
|
|
8603fd3257 | ||
|
|
648fc6e65c | ||
|
|
0c65e97fcf | ||
|
|
16d2154c73 | ||
|
|
b8e390db21 | ||
|
|
b2702062bc | ||
|
|
f11b32f188 | ||
|
|
d603607a90 | ||
|
|
6c0dffff56 | ||
|
|
9f3394aa1d | ||
|
|
74710664e3 | ||
|
|
0d0197fd2d | ||
|
|
c3b5b04b71 | ||
|
|
8b3164e107 | ||
|
|
79ff5aa148 | ||
|
|
652ce4a53d | ||
|
|
df0d6594b6 | ||
|
|
f73ecc6066 | ||
|
|
0f614e8460 | ||
|
|
f4bdd201a3 | ||
|
|
5130c83556 | ||
|
|
423ecc003b | ||
|
|
419f37a4a9 | ||
|
|
3d94ffb714 | ||
|
|
d40cfd30a2 | ||
|
|
3b4a92f66c | ||
|
|
f9dfbe3fa5 | ||
|
|
bc8bb08853 | ||
|
|
cc67a8fcdd | ||
|
|
f8cf88e6cd | ||
|
|
bc58c47a22 | ||
|
|
aa6879c48e | ||
|
|
4d26628f2a | ||
|
|
abaa70da99 | ||
|
|
6435c3520c | ||
|
|
b5c4c7ae61 | ||
|
|
5d96da3547 | ||
|
|
ed842f781a | ||
|
|
5e30e5e1de | ||
|
|
ce52a36db1 | ||
|
|
f5ef87b34b | ||
|
|
4c6f68c255 | ||
|
|
ae09b4c607 | ||
|
|
880d77922e | ||
|
|
1c227e81e4 | ||
|
|
541acad29a | ||
|
|
60f71648f3 | ||
|
|
270b8af39f | ||
|
|
31d3324701 | ||
|
|
dbe46d60fd | ||
|
|
d10ab96955 | ||
|
|
b2841e245a | ||
|
|
68c40f0c46 | ||
|
|
9ccd8d1602 | ||
|
|
ac3942975e | ||
|
|
b6455d40a7 | ||
|
|
2815ad042d | ||
|
|
2055961534 | ||
|
|
741eae34e9 | ||
|
|
50bd4cce6b | ||
|
|
0a5a8310fc | ||
|
|
cc43d5982a | ||
|
|
50675473ce | ||
|
|
de852b2a9f | ||
|
|
b7c712116b | ||
|
|
7dd4897c8c | ||
|
|
7469c0f5e3 | ||
|
|
144ea0c2fc | ||
|
|
a917ebdc76 | ||
|
|
2a389c74dc | ||
|
|
ef16d096f1 | ||
|
|
679455888b | ||
|
|
d4372ddae7 | ||
|
|
c3ef990a94 | ||
|
|
8ae9b7f5a2 | ||
|
|
106d80ef76 | ||
|
|
9422ba2718 | ||
|
|
8343f5c2db | ||
|
|
371c7efb04 | ||
|
|
92d67645ab | ||
|
|
a20e868970 | ||
|
|
dd853f6718 | ||
|
|
16a8ad996a | ||
|
|
e27885f0c8 | ||
|
|
f6ef48bf90 | ||
|
|
e282ca763d | ||
|
|
71016382dc | ||
|
|
d004933fae | ||
|
|
37512c50d8 | ||
|
|
0b61a5d40a | ||
|
|
5dd320f282 | ||
|
|
2a21db5fb6 | ||
|
|
b023593a2c | ||
|
|
5ccf2cae1f | ||
|
|
c2cb89ab73 | ||
|
|
b342759e06 | ||
|
|
93d99b0111 | ||
|
|
61e8d576d2 | ||
|
|
75c37a258e | ||
|
|
e964dae64b | ||
|
|
986d884b40 | ||
|
|
9557afabc6 | ||
|
|
ebe6b0d4c0 | ||
|
|
6e83fb7aef | ||
|
|
7a5ec2af12 | ||
|
|
ce1fde496c | ||
|
|
4b62c51fbf | ||
|
|
226ed3dd73 | ||
|
|
ab07dfb32c | ||
|
|
20c51c1aa4 | ||
|
|
232c2129a7 | ||
|
|
3620edbfc9 | ||
|
|
ad71d69149 | ||
|
|
f73f8ca7e7 | ||
|
|
16c701a71a | ||
|
|
8183b7b26a | ||
|
|
bd48c97eab | ||
|
|
925dc29a1f | ||
|
|
91777fd942 | ||
|
|
fbce8f81c7 | ||
|
|
d7c72c4d68 | ||
|
|
4faf535801 | ||
|
|
526ef7c6d8 | ||
|
|
798dff1a03 | ||
|
|
a4336776c9 | ||
|
|
418451cbd9 | ||
|
|
045fcfc5fa | ||
|
|
ef998577db | ||
|
|
a53345a3c9 | ||
|
|
ed8c09282d | ||
|
|
42197b5b5c | ||
|
|
374fc7035b | ||
|
|
9b796c7cc3 | ||
|
|
532edff642 | ||
|
|
6857252471 | ||
|
|
c229e19452 | ||
|
|
42bca09d16 | ||
|
|
9eacbfa659 | ||
|
|
f14e546dc6 | ||
|
|
684c64a1d9 | ||
|
|
6fdab959b1 | ||
|
|
c8487483ff | ||
|
|
a159b23dc0 | ||
|
|
5070a27a83 | ||
|
|
9ce73a6840 | ||
|
|
6e9928f20f | ||
|
|
b31d61afc5 | ||
|
|
5a99cb93cc | ||
|
|
d0bbebd25e | ||
|
|
4307d26606 | ||
|
|
0089c1ac6d | ||
|
|
2a7aac4930 | ||
|
|
a37b6d81ed | ||
|
|
1d09a6708a | ||
|
|
d3b6f484c8 | ||
|
|
039c6edb66 | ||
|
|
8b9f89eab2 | ||
|
|
1e2c17b170 | ||
|
|
a994966095 | ||
|
|
2bea581654 | ||
|
|
87377666aa | ||
|
|
9d07b2e141 | ||
|
|
5c312b49e2 | ||
|
|
f56efe45cd | ||
|
|
2332a58681 | ||
|
|
8c6dfaa196 | ||
|
|
3cfb04b60d | ||
|
|
e85fbfb952 | ||
|
|
80ee35d926 | ||
|
|
4796902b9c | ||
|
|
149e67c0f7 | ||
|
|
1d5214117f | ||
|
|
b8f248ca9c | ||
|
|
dfb71a03a5 | ||
|
|
961fdc8e72 | ||
|
|
c3d44663cd | ||
|
|
0081472489 | ||
|
|
cdf4f3a24b | ||
|
|
fb1d8e860f | ||
|
|
a3c526ec9a | ||
|
|
dee488d06d | ||
|
|
b29c7d8022 | ||
|
|
0725d207ec | ||
|
|
5a7599a88d | ||
|
|
59cd98db81 | ||
|
|
768488eb04 | ||
|
|
a6b1ad48c3 | ||
|
|
77299a68ed | ||
|
|
5e5705c73b | ||
|
|
e6229a3a13 | ||
|
|
5fbacb4ee4 | ||
|
|
c7f4e976ed | ||
|
|
419f2d966a | ||
|
|
d6c18db9e9 | ||
|
|
8fe49d9961 | ||
|
|
f536cfdab8 | ||
|
|
4d594acad5 | ||
|
|
800dfed5c1 | ||
|
|
54b823e401 | ||
|
|
52ec56d690 | ||
|
|
d4f8abfac1 | ||
|
|
d07c144316 | ||
|
|
dcd5189910 | ||
|
|
7b3afcca99 | ||
|
|
a22d03d028 | ||
|
|
d857338ad0 | ||
|
|
dcd5e34c6b |
@@ -11,8 +11,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 26
|
targetSdkVersion 26
|
||||||
versionCode 10105
|
versionCode 10106
|
||||||
versionName "1.1.5"
|
versionName "1.1.6"
|
||||||
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.4.8@zip'
|
tor 'org.briarproject:tor-android:0.3.5.8@zip'
|
||||||
tor 'org.briarproject:obfs4proxy-android:0.0.7@zip'
|
tor 'org.briarproject:obfs4proxy-android:0.0.9@zip'
|
||||||
|
|
||||||
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
|
annotationProcessor 'com.google.dagger:dagger-compiler:2.19'
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,15 @@ 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 {
|
||||||
|
|
||||||
@@ -89,20 +93,42 @@ 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);
|
||||||
File[] children = dataDir.listFiles();
|
@Nullable
|
||||||
if (children == null) {
|
File[] fileArray = dataDir.listFiles();
|
||||||
|
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 child : children) {
|
for (File file : fileArray) {
|
||||||
String name = child.getName();
|
String name = file.getName();
|
||||||
if (!name.equals("lib") && !name.equals("shared_prefs")) {
|
if (!name.equals("lib") && !name.equals("shared_prefs")) {
|
||||||
IoUtils.deleteFileOrDir(child);
|
files.add(file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
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").mkdir())
|
if (!new File(dataDir, "cache").mkdirs())
|
||||||
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,9 +19,7 @@ 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.BATTERY_STATUS_CHARGING;
|
import static android.os.BatteryManager.EXTRA_PLUGGED;
|
||||||
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;
|
||||||
|
|
||||||
@@ -48,9 +46,8 @@ 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_STATUS, -1);
|
int status = i.getIntExtra(EXTRA_PLUGGED, 0);
|
||||||
return status == BATTERY_STATUS_CHARGING ||
|
return status != 0;
|
||||||
status == BATTERY_STATUS_FULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -32,9 +32,6 @@ 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;
|
||||||
|
|
||||||
@@ -55,7 +52,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,
|
||||||
WAKE_LOCK_TAG, 1, MINUTES);
|
getWakeLockTag(), 1, MINUTES);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -87,4 +84,17 @@ 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
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;
|
||||||
@@ -32,6 +35,13 @@ 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) {
|
||||||
|
|||||||
@@ -112,6 +112,8 @@ 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();
|
||||||
@@ -128,6 +130,12 @@ 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());
|
||||||
@@ -140,6 +148,8 @@ 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();
|
||||||
|
|
||||||
@@ -153,6 +163,8 @@ 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
|
||||||
|
|||||||
@@ -1,46 +1,44 @@
|
|||||||
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.2.1:protos-26.2.1.jar:2f371f5b1f551e85ab08be4d6a2873471b3d44afd1ebf6aa3298f3b796bf691f',
|
'com.android.tools.analytics-library:protos:26.3.2:protos-26.3.2.jar:50238fb4298b297217b184b9cd93c14f83536fcee829eb0ca850bdb5b53251f0',
|
||||||
'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a',
|
'com.android.tools.analytics-library:shared:26.3.2:shared-26.3.2.jar:ddd80dcf21905018b7c0583ba72b7282f446084d4952422609a09fbf8237ef71',
|
||||||
'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75',
|
'com.android.tools.analytics-library:tracker:26.3.2:tracker-26.3.2.jar:28c575d2d1af003e96d7b375a668ee10b2673a2dd0f6438750aa8c3b42e7d0ad',
|
||||||
'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e',
|
'com.android.tools.build:apksig:3.3.2:apksig-3.3.2.jar:84c4aaa20127c6c1fe6bdd334b3f5df71f54ad080be9029c8a10f43b6a908acd',
|
||||||
'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5',
|
'com.android.tools.build:apkzlib:3.3.2:apkzlib-3.3.2.jar:d34e523278e5dff565eba3ef3c089d515b2b5cc7b47dc77e2f3465e5e47176ac',
|
||||||
'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa',
|
'com.android.tools.build:builder-model:3.3.2:builder-model-3.3.2.jar:055e3db0ecee9e06b9f024034999a29cd92cb1885207b37542126bd8bcc57f46',
|
||||||
'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff',
|
'com.android.tools.build:builder-test-api:3.3.2:builder-test-api-3.3.2.jar:0b2e4cd7615bbcad14a3c91fe45ae26693508d06e40ba06c5968b8bc24416618',
|
||||||
'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be',
|
'com.android.tools.build:builder:3.3.2:builder-3.3.2.jar:65649704da7aef0487235fd326f0f2e99ed5cf958e80f204496e6e08a42bd9f5',
|
||||||
'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83',
|
'com.android.tools.build:gradle-api:3.3.2:gradle-api-3.3.2.jar:3cbd47e41bb70330dd72ec2c9fe51e6173554b484a03829b5a2de9e00841e040',
|
||||||
'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d',
|
'com.android.tools.build:manifest-merger:26.3.2:manifest-merger-26.3.2.jar:05c4a6d8b02fb9f08744876477d0a68547c03a8a9069b1f086684fa04af97c33',
|
||||||
'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6',
|
'com.android.tools.ddms:ddmlib:26.3.2:ddmlib-26.3.2.jar:d248da8a563d6e46d2c7ebbf371a4877e00510f4ca763c0bb272d5a281bf8b85',
|
||||||
'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b',
|
'com.android.tools.external.com-intellij:intellij-core:26.3.2:intellij-core-26.3.2.jar:6c5ecc968230e9f4dcd0fef28885379feace1f0cd8130de6f61d649c86139bf3',
|
||||||
'com.android.tools.external.com-intellij:intellij-core:26.2.1:intellij-core-26.2.1.jar:4925ad1892c2687cb1a63427d440ef519c8c59215fefe0dc5d541d5d411fcafe',
|
'com.android.tools.external.com-intellij:kotlin-compiler:26.3.2:kotlin-compiler-26.3.2.jar:1007d9b07ccb49cd8eaf30fda10ed4681d4714f2f9ab2ecda39b4e539cc51bbe',
|
||||||
'com.android.tools.external.com-intellij:kotlin-compiler:26.2.1:kotlin-compiler-26.2.1.jar:daa064fd708f340ee25fb9823c4c74104ac77f1370b76d907eb9ae6daec0a2ae',
|
'com.android.tools.external.org-jetbrains:uast:26.3.2:uast-26.3.2.jar:5d1833e562ea4f38a89708dfde695f0a162cbd39d003d3dde818c3fdc2b05317',
|
||||||
'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b',
|
'com.android.tools.layoutlib:layoutlib-api:26.3.2:layoutlib-api-26.3.2.jar:d7e61e874ab95f5c350dd38b6a95b5c9dbe0083a02001884264cdb390cb255b8',
|
||||||
'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade',
|
'com.android.tools.lint:lint-api:26.3.2:lint-api-26.3.2.jar:5867dfd7fb4a4e161a816a5d29d045f9b542d34594c00a1efec46fb4cd0e1033',
|
||||||
'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e',
|
'com.android.tools.lint:lint-checks:26.3.2:lint-checks-26.3.2.jar:4b163b9c93790d2771e92ba8de58a0d9e0671ffcf2ccef3cf496efd442e27517',
|
||||||
'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6',
|
'com.android.tools.lint:lint-gradle-api:26.3.2:lint-gradle-api-26.3.2.jar:54cb282e0c054f9bed3f51302ce08b003c8ab7961dfd5a4f6de26c23cc23062f',
|
||||||
'com.android.tools.lint:lint-gradle-api:26.2.1:lint-gradle-api-26.2.1.jar:2283e7af32e301565f2a797e531f0fc8c648077d457afb3ffdddbee638976c2f',
|
'com.android.tools.lint:lint-gradle:26.3.2:lint-gradle-26.3.2.jar:bb139615f4ce97d42cc394b9389b49b76a6eb85be6785a5d272991543b519013',
|
||||||
'com.android.tools.lint:lint-gradle:26.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb',
|
'com.android.tools.lint:lint:26.3.2:lint-26.3.2.jar:ef7b369f8a56a92ccb0f4c1c357666b9339e4a711a9d84747d446441746cfe4e',
|
||||||
'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877',
|
'com.android.tools:annotations:26.3.2:annotations-26.3.2.jar:5bcce8e98b6a2f5ccf13ebcefd8f734e0b35f8b19e456575665631442ce1f7a1',
|
||||||
'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5',
|
'com.android.tools:common:26.3.2:common-26.3.2.jar:d9f8e7f0669e9a701568e3db6a87c89cf12d8fa6811c9991e969f950215ecfac',
|
||||||
'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24',
|
'com.android.tools:dvlib:26.3.2:dvlib-26.3.2.jar:d84aad56161c7773579303d69714ded6897c64c6ddfd7d456e453231e4dfe811',
|
||||||
'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5',
|
'com.android.tools:repository:26.3.2:repository-26.3.2.jar:da611eeb06e9ab8750d25b9e2901e10db8e5ec6304eb4c8b7103d39e0921ea40',
|
||||||
'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa',
|
'com.android.tools:sdk-common:26.3.2:sdk-common-26.3.2.jar:82823a3bf25e64fac33a286490f0cf5ac50c2cdb3c540149b030896bb44bf96c',
|
||||||
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1',
|
'com.android.tools:sdklib:26.3.2:sdklib-26.3.2.jar:424d15492af67321900963238646d27495ab60de2a5b19e6a416963bc5d6932b',
|
||||||
'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',
|
||||||
@@ -68,21 +66,22 @@ 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.7:obfs4proxy-android-0.0.7.zip:abdfb5d889d848de9bf214f9276abbf454808a505b870819eccc9a9e985bf617',
|
'org.briarproject:obfs4proxy-android:0.0.9:obfs4proxy-android-0.0.9.zip:9b7e9181535ea8d8bbe8ae6338e08cf4c5fc1e357a779393e0ce49586d459ae0',
|
||||||
'org.briarproject:tor-android:0.3.4.8:tor-android-0.3.4.8.zip:989a0352d9d8d8172cd6c2137654e165e5d2beb10ed1211bab3814e224ad1926',
|
'org.briarproject:tor-android:0.3.5.8:tor-android-0.3.5.8.zip:42a13a6f185be1a62f42e3f30ce66a3c099ac5ec890a65e7593111b65b44a54a',
|
||||||
'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.codehaus.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
|
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
|
||||||
|
'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.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
|
'org.jetbrains.kotlin:kotlin-reflect:1.3.21:kotlin-reflect-1.3.21.jar:a3065c822633191e0a3e3ee12a29bec234fc4b2864a6bb87ef48cce3e9e0c26a',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
|
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21:kotlin-stdlib-common-1.3.21.jar:cea61f7b611895e64f58569a9757fc0ab0d582f107211e1930e0ce2a0add52a7',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21:kotlin-stdlib-jdk7-1.3.21.jar:a87875604fd42140da6938ae4d35ee61081f4482536efc6d2615b8b626a198af',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
|
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21:kotlin-stdlib-jdk8-1.3.21.jar:5823ed66ac122a1c55442ebca5a209a843ccd87f562edc31a787f3d2e47f74d4',
|
||||||
'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b',
|
'org.jetbrains.kotlin:kotlin-stdlib:1.3.21:kotlin-stdlib-1.3.21.jar:38ba2370d9f06f50433e06b2ca775b94473c2e2785f410926079ab793c72b034',
|
||||||
'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',
|
||||||
|
|||||||
@@ -18,11 +18,10 @@ 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);
|
Contact contact, AuthorId local);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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);
|
||||||
|
|||||||
@@ -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,24 +16,28 @@ 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;
|
||||||
private final boolean verified, active;
|
@Nullable
|
||||||
|
private final byte[] handshakePublicKey;
|
||||||
|
private final boolean verified;
|
||||||
|
|
||||||
public Contact(ContactId id, Author author, AuthorId localAuthorId,
|
public Contact(ContactId id, Author author, @Nullable String alias,
|
||||||
@Nullable String alias, boolean verified, boolean active) {
|
@Nullable byte[] handshakePublicKey, boolean verified) {
|
||||||
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() {
|
||||||
@@ -44,21 +48,18 @@ public class Contact {
|
|||||||
return author;
|
return author;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AuthorId getLocalAuthorId() {
|
|
||||||
return localAuthorId;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getAlias() {
|
public String getAlias() {
|
||||||
return alias;
|
return alias;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isVerified() {
|
@Nullable
|
||||||
return verified;
|
public byte[] getHandshakePublicKey() {
|
||||||
|
return handshakePublicKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive() {
|
public boolean isVerified() {
|
||||||
return active;
|
return verified;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,20 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
@@ -18,31 +18,30 @@ public interface ContactExchangeTask {
|
|||||||
byte PROTOCOL_VERSION = 1;
|
byte PROTOCOL_VERSION = 1;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for deriving Alice's header key from the master secret.
|
* Label for deriving Alice's header key from the master key.
|
||||||
*/
|
*/
|
||||||
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 secret.
|
* Label for deriving Bob's header key from the master key.
|
||||||
*/
|
*/
|
||||||
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 secret.
|
* Label for deriving Alice's key binding nonce from the master key.
|
||||||
*/
|
*/
|
||||||
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 secret.
|
* Label for deriving Bob's key binding nonce from the master key.
|
||||||
*/
|
*/
|
||||||
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(ContactExchangeListener listener,
|
void startExchange(LocalAuthor localAuthor, SecretKey masterKey,
|
||||||
LocalAuthor localAuthor, SecretKey masterSecret,
|
|
||||||
DuplexTransportConnection conn, TransportId transportId,
|
DuplexTransportConnection conn, TransportId transportId,
|
||||||
boolean alice);
|
boolean alice);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,61 +24,80 @@ public interface ContactManager {
|
|||||||
void registerContactHook(ContactHook hook);
|
void registerContactHook(ContactHook hook);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a contact associated with the given local and remote pseudonyms,
|
* Stores a contact with the given pseudonym, derives and stores transport
|
||||||
* derives and stores transport keys for each transport, and returns an ID
|
* keys for each transport, and returns an ID for the contact.
|
||||||
* 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 remote, AuthorId local,
|
ContactId addContact(Transaction txn, Author a, SecretKey rootKey,
|
||||||
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 remoteAuthorId
|
* Returns the contact with the given ID.
|
||||||
* that was added by the LocalAuthor with the given localAuthorId
|
|
||||||
*
|
|
||||||
* @throws org.briarproject.bramble.api.db.NoSuchContactException
|
|
||||||
*/
|
*/
|
||||||
Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
Contact getContact(AuthorId a) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the contact with the given remoteAuthorId
|
* Returns the contact with the given ID.
|
||||||
* that was added by the LocalAuthor with the given localAuthorId
|
|
||||||
*
|
|
||||||
* @throws org.briarproject.bramble.api.db.NoSuchContactException
|
|
||||||
*/
|
*/
|
||||||
Contact getContact(Transaction txn, AuthorId remoteAuthorId,
|
Contact getContact(Transaction txn, AuthorId a) throws DbException;
|
||||||
AuthorId localAuthorId) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns all active contacts.
|
* Returns all active contacts.
|
||||||
*/
|
*/
|
||||||
Collection<Contact> getActiveContacts() throws DbException;
|
Collection<Contact> getContacts() throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes a contact and all associated state.
|
* Removes a contact and all associated state.
|
||||||
@@ -90,12 +109,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -109,16 +122,14 @@ public interface ContactManager {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if a contact with this name and public key already exists
|
* Returns true if a contact with this pseudonym already exists.
|
||||||
*/
|
*/
|
||||||
boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
|
boolean contactExists(Transaction txn, AuthorId a) throws DbException;
|
||||||
AuthorId localAuthorId) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if a contact with this name and public key already exists
|
* Returns true if a contact with this pseudonym already exists.
|
||||||
*/
|
*/
|
||||||
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
|
boolean contactExists(AuthorId a) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the {@link AuthorInfo} for the given author.
|
* Returns the {@link AuthorInfo} for the given author.
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,18 +14,12 @@ 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, boolean active) {
|
public ContactAddedEvent(ContactId contactId) {
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
this.active = active;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ContactId getContactId() {
|
public ContactId getContactId() {
|
||||||
return contactId;
|
return contactId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive() {
|
|
||||||
return active;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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/akwizgran/briar-spec/blob/master/protocols/BQP.md
|
* https://code.briarproject.org/briar/briar-spec/blob/master/protocols/BQP.md
|
||||||
*/
|
*/
|
||||||
public interface KeyAgreementCrypto {
|
public interface KeyAgreementCrypto {
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,45 @@
|
|||||||
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/akwizgran/briar-spec/blob/master/protocols/BTP.md
|
* https://code.briarproject.org/briar/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
|
||||||
* rotation period from the given master secret.
|
* time period from the given root key.
|
||||||
*
|
*
|
||||||
* @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 master,
|
TransportKeys deriveTransportKeys(TransportId t, SecretKey rootKey,
|
||||||
long rotationPeriod, boolean alice, boolean active);
|
long timePeriod, boolean alice, boolean active);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotates the given transport keys to the given rotation period. If the
|
* Rotates the given transport keys to the given time period. If the keys
|
||||||
* keys are for the given period or any later period they are not rotated.
|
* are for the given period or any later period they are not rotated.
|
||||||
*/
|
*/
|
||||||
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,6 +2,8 @@ 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;
|
||||||
@@ -20,8 +22,11 @@ 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.KeySet;
|
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
||||||
|
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;
|
||||||
@@ -97,17 +102,31 @@ public interface DatabaseComponent {
|
|||||||
NullableDbCallable<R, E> task) throws DbException, E;
|
NullableDbCallable<R, E> task) throws DbException, E;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a contact associated with the given local and remote pseudonyms,
|
* Stores a contact with the given pseudonym and returns an ID for the
|
||||||
* and returns an ID for the contact.
|
* contact.
|
||||||
*/
|
*/
|
||||||
ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
ContactId addContact(Transaction txn, Author a, boolean verified)
|
||||||
boolean verified, boolean active) throws DbException;
|
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.
|
||||||
*/
|
*/
|
||||||
@@ -119,6 +138,12 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -129,27 +154,39 @@ 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.
|
||||||
*/
|
*/
|
||||||
KeySetId addTransportKeys(Transaction txn, ContactId c,
|
TransportKeySetId addTransportKeys(Transaction txn, ContactId c,
|
||||||
TransportKeys k) throws DbException;
|
TransportKeys k) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given contact for the given
|
* Returns true if the database contains the given contact.
|
||||||
* local pseudonym.
|
* <p/>
|
||||||
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
boolean containsContact(Transaction txn, AuthorId remote, AuthorId local)
|
boolean containsContact(Transaction txn, AuthorId a) throws DbException;
|
||||||
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,
|
||||||
@@ -215,6 +252,13 @@ 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/>
|
||||||
@@ -222,22 +266,6 @@ 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/>
|
||||||
@@ -269,6 +297,14 @@ 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/>
|
||||||
@@ -417,6 +453,14 @@ 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/>
|
||||||
@@ -429,14 +473,20 @@ public interface DatabaseComponent {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<KeySet> getTransportKeys(Transaction txn, TransportId t)
|
Collection<TransportKeySet> 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, KeySetId k)
|
void incrementStreamCounter(Transaction txn, TransportId t,
|
||||||
throws DbException;
|
TransportKeySetId k) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Merges the given metadata with the existing metadata for the given
|
* Merges the given metadata with the existing metadata for the given
|
||||||
@@ -491,6 +541,12 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -501,6 +557,12 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -509,20 +571,14 @@ 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, KeySetId k)
|
void removeTransportKeys(Transaction txn, TransportId t,
|
||||||
throws DbException;
|
TransportKeySetId k) 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.
|
||||||
*/
|
*/
|
||||||
@@ -553,21 +609,36 @@ public interface DatabaseComponent {
|
|||||||
Collection<MessageId> dependencies) throws DbException;
|
Collection<MessageId> dependencies) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the reordering window for the given key set and transport in the
|
* Sets the reordering window for the given transport key set in the given
|
||||||
* given rotation period.
|
* time period.
|
||||||
*/
|
*/
|
||||||
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
|
void setReorderingWindow(Transaction txn, TransportKeySetId k,
|
||||||
long rotationPeriod, long base, byte[] bitmap) throws DbException;
|
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(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, KeySetId k)
|
void setTransportKeysActive(Transaction txn, TransportId t,
|
||||||
|
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<KeySet> keys)
|
void updateTransportKeys(Transaction txn, Collection<TransportKeySet> keys)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,4 @@ public interface DatabaseConfig {
|
|||||||
File getDatabaseDirectory();
|
File getDatabaseDirectory();
|
||||||
|
|
||||||
File getDatabaseKeyDirectory();
|
File getDatabaseKeyDirectory();
|
||||||
|
|
||||||
long getMaxSize();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
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.
|
||||||
*/
|
*/
|
||||||
@@ -17,7 +19,7 @@ public class Transaction {
|
|||||||
private final Object txn;
|
private final Object txn;
|
||||||
private final boolean readOnly;
|
private final boolean readOnly;
|
||||||
|
|
||||||
private List<Event> events = null;
|
private List<CommitAction> actions = null;
|
||||||
private boolean committed = false;
|
private boolean committed = false;
|
||||||
|
|
||||||
public Transaction(Object txn, boolean readOnly) {
|
public Transaction(Object txn, boolean readOnly) {
|
||||||
@@ -42,19 +44,27 @@ 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.
|
* committed. The event will be broadcast on the {@link EventExecutor}.
|
||||||
*/
|
*/
|
||||||
public void attach(Event e) {
|
public void attach(Event e) {
|
||||||
if (events == null) events = new ArrayList<>();
|
if (actions == null) actions = new ArrayList<>();
|
||||||
events.add(e);
|
actions.add(new EventAction(e));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns any events attached to the transaction.
|
* Attaches a task to be executed when the transaction has been
|
||||||
|
* committed. The task will be run on the {@link EventExecutor}.
|
||||||
*/
|
*/
|
||||||
public List<Event> getEvents() {
|
public void attach(Runnable r) {
|
||||||
if (events == null) return Collections.emptyList();
|
if (actions == null) actions = new ArrayList<>();
|
||||||
return events;
|
actions.add(new TaskAction(r));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns any actions attached to the transaction.
|
||||||
|
*/
|
||||||
|
public List<CommitAction> getActions() {
|
||||||
|
return actions == null ? emptyList() : actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -16,7 +16,8 @@ public interface EventBus {
|
|||||||
void removeListener(EventListener l);
|
void removeListener(EventListener l);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notifies all listeners of an event.
|
* Asynchronously notifies all listeners of an event. Listeners are
|
||||||
|
* notified on the {@link EventExecutor}.
|
||||||
*/
|
*/
|
||||||
void broadcast(Event e);
|
void broadcast(Event e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
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 {
|
||||||
|
}
|
||||||
@@ -12,5 +12,6 @@ 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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -12,6 +15,8 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
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,
|
||||||
@@ -19,6 +24,22 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -28,6 +49,22 @@ 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.
|
||||||
|
|||||||
@@ -40,8 +40,8 @@ public interface KeyAgreementConstants {
|
|||||||
"org.briarproject.bramble.keyagreement/SHARED_SECRET";
|
"org.briarproject.bramble.keyagreement/SHARED_SECRET";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for deriving the master secret.
|
* Label for deriving the master key.
|
||||||
*/
|
*/
|
||||||
String MASTER_SECRET_LABEL =
|
String MASTER_KEY_LABEL =
|
||||||
"org.briarproject.bramble.keyagreement/MASTER_SECRET";
|
"org.briarproject.bramble.keyagreement/MASTER_SECRET";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ 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;
|
||||||
|
|||||||
@@ -0,0 +1,57 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,30 +1,35 @@
|
|||||||
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 rotation period.
|
* given transport in a given time period.
|
||||||
*/
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
public class IncomingKeys {
|
public class IncomingKeys {
|
||||||
|
|
||||||
private final SecretKey tagKey, headerKey;
|
private final SecretKey tagKey, headerKey;
|
||||||
private final long rotationPeriod, windowBase;
|
private final long timePeriod, windowBase;
|
||||||
private final byte[] windowBitmap;
|
private final byte[] windowBitmap;
|
||||||
|
|
||||||
public IncomingKeys(SecretKey tagKey, SecretKey headerKey,
|
public IncomingKeys(SecretKey tagKey, SecretKey headerKey,
|
||||||
long rotationPeriod) {
|
long timePeriod) {
|
||||||
this(tagKey, headerKey, rotationPeriod, 0,
|
this(tagKey, headerKey, timePeriod, 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 rotationPeriod, long windowBase, byte[] windowBitmap) {
|
long timePeriod, long windowBase, byte[] windowBitmap) {
|
||||||
this.tagKey = tagKey;
|
this.tagKey = tagKey;
|
||||||
this.headerKey = headerKey;
|
this.headerKey = headerKey;
|
||||||
this.rotationPeriod = rotationPeriod;
|
this.timePeriod = timePeriod;
|
||||||
this.windowBase = windowBase;
|
this.windowBase = windowBase;
|
||||||
this.windowBitmap = windowBitmap;
|
this.windowBitmap = windowBitmap;
|
||||||
}
|
}
|
||||||
@@ -37,8 +42,8 @@ public class IncomingKeys {
|
|||||||
return headerKey;
|
return headerKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getRotationPeriod() {
|
public long getTimePeriod() {
|
||||||
return rotationPeriod;
|
return timePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getWindowBase() {
|
public long getWindowBase() {
|
||||||
|
|||||||
@@ -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, KeySetId> addContact(Transaction txn, ContactId c,
|
Map<TransportId, TransportKeySetId> addContact(Transaction txn, ContactId c,
|
||||||
SecretKey master, long timestamp, boolean alice, boolean active)
|
SecretKey rootKey, 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, KeySetId> keys)
|
void activateKeys(Transaction txn, Map<TransportId, TransportKeySetId> keys)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,47 +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 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,27 +1,32 @@
|
|||||||
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 rotation period.
|
* transport in a given time period.
|
||||||
*/
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
public class OutgoingKeys {
|
public class OutgoingKeys {
|
||||||
|
|
||||||
private final SecretKey tagKey, headerKey;
|
private final SecretKey tagKey, headerKey;
|
||||||
private final long rotationPeriod, streamCounter;
|
private final long timePeriod, streamCounter;
|
||||||
private final boolean active;
|
private final boolean active;
|
||||||
|
|
||||||
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
|
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
|
||||||
long rotationPeriod, boolean active) {
|
long timePeriod, boolean active) {
|
||||||
this(tagKey, headerKey, rotationPeriod, 0, active);
|
this(tagKey, headerKey, timePeriod, 0, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
|
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
|
||||||
long rotationPeriod, long streamCounter, boolean active) {
|
long timePeriod, long streamCounter, boolean active) {
|
||||||
this.tagKey = tagKey;
|
this.tagKey = tagKey;
|
||||||
this.headerKey = headerKey;
|
this.headerKey = headerKey;
|
||||||
this.rotationPeriod = rotationPeriod;
|
this.timePeriod = timePeriod;
|
||||||
this.streamCounter = streamCounter;
|
this.streamCounter = streamCounter;
|
||||||
this.active = active;
|
this.active = active;
|
||||||
}
|
}
|
||||||
@@ -34,8 +39,8 @@ public class OutgoingKeys {
|
|||||||
return headerKey;
|
return headerKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getRotationPeriod() {
|
public long getTimePeriod() {
|
||||||
return rotationPeriod;
|
return timePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getStreamCounter() {
|
public long getStreamCounter() {
|
||||||
|
|||||||
@@ -2,8 +2,13 @@ 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;
|
||||||
|
|||||||
@@ -82,30 +82,58 @@ public interface TransportConstants {
|
|||||||
int REORDERING_WINDOW_SIZE = 32;
|
int REORDERING_WINDOW_SIZE = 32;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Label for deriving Alice's initial tag key from the master secret.
|
* Label for deriving Alice's initial tag key from the root key in
|
||||||
|
* 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 master secret.
|
* Label for deriving Bob's initial tag key from the root key in rotation
|
||||||
|
* 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 master secret.
|
* Label for deriving Alice's initial header key from the root key in
|
||||||
|
* 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 master secret.
|
* Label for deriving Bob's initial header key from the root key in
|
||||||
|
* 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 key rotation.
|
* Label for deriving the next period's key in rotation mode.
|
||||||
*/
|
*/
|
||||||
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";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,18 +5,19 @@ 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 set of transport
|
* Type-safe wrapper for an integer that uniquely identifies a
|
||||||
* keys within the scope of the local device.
|
* {@link TransportKeySet set of transport keys} within the scope of the local
|
||||||
|
* 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 KeySetId {
|
public class TransportKeySetId {
|
||||||
|
|
||||||
private final int id;
|
private final int id;
|
||||||
|
|
||||||
public KeySetId(int id) {
|
public TransportKeySetId(int id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -31,6 +32,7 @@ public class KeySetId {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object o) {
|
public boolean equals(Object o) {
|
||||||
return o instanceof KeySetId && id == ((KeySetId) o).id;
|
return o instanceof TransportKeySetId &&
|
||||||
|
id == ((TransportKeySetId) o).id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,52 +1,20 @@
|
|||||||
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.
|
|
||||||
*/
|
|
||||||
public class TransportKeys {
|
|
||||||
|
|
||||||
private final TransportId transportId;
|
/**
|
||||||
private final IncomingKeys inPrev, inCurr, inNext;
|
* Keys for communicating with a given contact over a given transport. Unlike
|
||||||
private final OutgoingKeys outCurr;
|
* {@link HandshakeKeys} these keys provide forward secrecy.
|
||||||
|
*/
|
||||||
|
@Immutable
|
||||||
|
@NotNullByDefault
|
||||||
|
public class TransportKeys extends AbstractTransportKeys {
|
||||||
|
|
||||||
public TransportKeys(TransportId transportId, IncomingKeys inPrev,
|
public TransportKeys(TransportId transportId, IncomingKeys inPrev,
|
||||||
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
|
IncomingKeys inCurr, IncomingKeys inNext, OutgoingKeys outCurr) {
|
||||||
if (inPrev.getRotationPeriod() != inCurr.getRotationPeriod() - 1)
|
super(transportId, inPrev, inCurr, inNext, outCurr);
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,6 +38,13 @@ 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,
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
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;
|
||||||
@@ -25,6 +29,7 @@ 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;
|
||||||
@@ -41,6 +46,7 @@ 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();
|
||||||
@@ -140,6 +146,35 @@ 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();
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ class ContactGroupFactoryImpl implements ContactGroupFactory {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Group createContactGroup(ClientId clientId, int majorVersion,
|
public Group createContactGroup(ClientId clientId, int majorVersion,
|
||||||
Contact contact) {
|
Contact contact, AuthorId local) {
|
||||||
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);
|
||||||
|
|||||||
@@ -2,10 +2,11 @@ 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;
|
||||||
@@ -13,6 +14,7 @@ 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;
|
||||||
@@ -63,6 +65,7 @@ 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;
|
||||||
@@ -71,18 +74,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 masterSecret;
|
private volatile SecretKey masterKey;
|
||||||
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, Clock clock,
|
RecordWriterFactory recordWriterFactory, EventBus eventBus,
|
||||||
ConnectionManager connectionManager, ContactManager contactManager,
|
Clock clock, ConnectionManager connectionManager,
|
||||||
|
ContactManager contactManager,
|
||||||
TransportPropertyManager transportPropertyManager,
|
TransportPropertyManager transportPropertyManager,
|
||||||
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
|
CryptoComponent crypto, StreamReaderFactory streamReaderFactory,
|
||||||
StreamWriterFactory streamWriterFactory) {
|
StreamWriterFactory streamWriterFactory) {
|
||||||
@@ -90,6 +93,7 @@ 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;
|
||||||
@@ -100,15 +104,13 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void startExchange(ContactExchangeListener listener,
|
public void startExchange(LocalAuthor localAuthor, SecretKey masterKey,
|
||||||
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.masterSecret = masterSecret;
|
this.masterKey = masterKey;
|
||||||
this.alice = alice;
|
this.alice = alice;
|
||||||
start();
|
start();
|
||||||
}
|
}
|
||||||
@@ -123,8 +125,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,15 +136,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);
|
||||||
listener.contactExchangeFailed();
|
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||||
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,
|
SecretKey aliceHeaderKey = crypto.deriveKey(ALICE_KEY_LABEL, masterKey,
|
||||||
masterSecret, new byte[] {PROTOCOL_VERSION});
|
new byte[] {PROTOCOL_VERSION});
|
||||||
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterSecret,
|
SecretKey bobHeaderKey = crypto.deriveKey(BOB_KEY_LABEL, masterKey,
|
||||||
new byte[] {PROTOCOL_VERSION});
|
new byte[] {PROTOCOL_VERSION});
|
||||||
|
|
||||||
// Create the readers
|
// Create the readers
|
||||||
@@ -161,9 +163,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, masterSecret,
|
byte[] aliceNonce = crypto.mac(ALICE_NONCE_LABEL, masterKey,
|
||||||
new byte[] {PROTOCOL_VERSION});
|
new byte[] {PROTOCOL_VERSION});
|
||||||
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterSecret,
|
byte[] bobNonce = crypto.mac(BOB_NONCE_LABEL, masterKey,
|
||||||
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;
|
||||||
@@ -196,7 +198,7 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
listener.contactExchangeFailed();
|
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||||
tryToClose(conn);
|
tryToClose(conn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -204,7 +206,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");
|
||||||
listener.contactExchangeFailed();
|
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||||
tryToClose(conn);
|
tryToClose(conn);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -221,15 +223,17 @@ class ContactExchangeTaskImpl extends Thread implements ContactExchangeTask {
|
|||||||
conn);
|
conn);
|
||||||
// Pseudonym exchange succeeded
|
// Pseudonym exchange succeeded
|
||||||
LOG.info("Pseudonym exchange succeeded");
|
LOG.info("Pseudonym exchange succeeded");
|
||||||
listener.contactExchangeSucceeded(remoteInfo.author);
|
eventBus.broadcast(
|
||||||
|
new ContactExchangeSucceededEvent(remoteInfo.author));
|
||||||
} catch (ContactExistsException e) {
|
} catch (ContactExistsException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
tryToClose(conn);
|
tryToClose(conn);
|
||||||
listener.duplicateContact(remoteInfo.author);
|
eventBus.broadcast(
|
||||||
|
new ContactExchangeFailedEvent(remoteInfo.author));
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
tryToClose(conn);
|
tryToClose(conn);
|
||||||
listener.contactExchangeFailed();
|
eventBus.broadcast(new ContactExchangeFailedEvent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,8 +293,7 @@ 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,
|
||||||
localAuthor.getId(), masterSecret, timestamp, alice,
|
masterKey, timestamp, alice, true, true);
|
||||||
true, true);
|
|
||||||
transportPropertyManager.addRemoteProperties(txn, contactId,
|
transportPropertyManager.addRemoteProperties(txn, contactId,
|
||||||
remoteProperties);
|
remoteProperties);
|
||||||
return contactId;
|
return contactId;
|
||||||
|
|||||||
@@ -3,10 +3,11 @@ 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;
|
||||||
@@ -16,16 +17,20 @@ 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;
|
||||||
@@ -36,6 +41,12 @@ 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;
|
||||||
@@ -56,66 +67,94 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
|
public ContactId addContact(Transaction txn, Author a, SecretKey rootKey,
|
||||||
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, remote, local, master, timestamp, alice,
|
addContact(txn, a, rootKey, 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 remoteAuthorId, AuthorId localAuthorId)
|
public Contact getContact(AuthorId a) throws DbException {
|
||||||
throws DbException {
|
return db.transactionWithResult(true, txn -> getContact(txn, a));
|
||||||
return db.transactionWithResult(true, txn ->
|
|
||||||
getContact(txn, remoteAuthorId, localAuthorId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Contact getContact(Transaction txn, AuthorId remoteAuthorId,
|
public Contact getContact(Transaction txn, AuthorId a) throws DbException {
|
||||||
AuthorId localAuthorId) throws DbException {
|
return db.getContact(txn, a);
|
||||||
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> getActiveContacts() throws DbException {
|
public Collection<Contact> getContacts() throws DbException {
|
||||||
Collection<Contact> contacts =
|
return db.transactionWithResult(true, db::getContacts);
|
||||||
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
|
||||||
@@ -123,12 +162,6 @@ 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 {
|
||||||
@@ -147,16 +180,14 @@ class ContactManagerImpl implements ContactManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
|
public boolean contactExists(Transaction txn, AuthorId a)
|
||||||
AuthorId localAuthorId) throws DbException {
|
throws DbException {
|
||||||
return db.containsContact(txn, remoteAuthorId, localAuthorId);
|
return db.containsContact(txn, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean contactExists(AuthorId remoteAuthorId,
|
public boolean contactExists(AuthorId a) throws DbException {
|
||||||
AuthorId localAuthorId) throws DbException {
|
return db.transactionWithResult(true, txn -> contactExists(txn, a));
|
||||||
return db.transactionWithResult(true, txn ->
|
|
||||||
contactExists(txn, remoteAuthorId, localAuthorId));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -178,12 +209,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);
|
||||||
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
|
if (db.containsContact(txn, authorId)) {
|
||||||
if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN);
|
Contact c = db.getContact(txn, authorId);
|
||||||
if (contacts.size() > 1) throw new AssertionError();
|
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
|
||||||
Contact c = contacts.iterator().next();
|
else return new AuthorInfo(UNVERIFIED, c.getAlias());
|
||||||
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
|
}
|
||||||
else return new AuthorInfo(UNVERIFIED, c.getAlias());
|
return new AuthorInfo(UNKNOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,18 +4,22 @@ 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;
|
||||||
@@ -24,6 +28,9 @@ 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 {
|
||||||
|
|
||||||
@@ -36,45 +43,44 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TransportKeys deriveTransportKeys(TransportId t,
|
public TransportKeys deriveTransportKeys(TransportId t,
|
||||||
SecretKey master, long rotationPeriod, boolean alice,
|
SecretKey rootKey, long timePeriod, boolean weAreAlice,
|
||||||
boolean active) {
|
boolean active) {
|
||||||
// Keys for the previous period are derived from the master secret
|
// Keys for the previous period are derived from the root key
|
||||||
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
|
SecretKey inTagPrev = deriveTagKey(rootKey, t, !weAreAlice);
|
||||||
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
|
SecretKey inHeaderPrev = deriveHeaderKey(rootKey, t, !weAreAlice);
|
||||||
SecretKey outTagPrev = deriveTagKey(master, t, alice);
|
SecretKey outTagPrev = deriveTagKey(rootKey, t, weAreAlice);
|
||||||
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
|
SecretKey outHeaderPrev = deriveHeaderKey(rootKey, t, weAreAlice);
|
||||||
// Derive the keys for the current and next periods
|
// Derive the keys for the current and next periods
|
||||||
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
|
SecretKey inTagCurr = rotateKey(inTagPrev, timePeriod);
|
||||||
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
|
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, timePeriod);
|
||||||
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
|
SecretKey inTagNext = rotateKey(inTagCurr, timePeriod + 1);
|
||||||
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
|
SecretKey inHeaderNext = rotateKey(inHeaderCurr, timePeriod + 1);
|
||||||
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
|
SecretKey outTagCurr = rotateKey(outTagPrev, timePeriod);
|
||||||
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
|
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, timePeriod);
|
||||||
// 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,
|
||||||
rotationPeriod - 1);
|
timePeriod - 1);
|
||||||
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
|
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
|
||||||
rotationPeriod);
|
timePeriod);
|
||||||
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
|
||||||
rotationPeriod + 1);
|
timePeriod + 1);
|
||||||
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
|
||||||
rotationPeriod, active);
|
timePeriod, 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,
|
public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) {
|
||||||
long rotationPeriod) {
|
if (k.getTimePeriod() >= timePeriod) return k;
|
||||||
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.getRotationPeriod();
|
long startPeriod = outCurr.getTimePeriod();
|
||||||
boolean active = outCurr.isActive();
|
boolean active = outCurr.isActive();
|
||||||
// Rotate the keys
|
// Rotate the keys
|
||||||
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
|
for (long p = startPeriod + 1; p <= timePeriod; p++) {
|
||||||
inPrev = inCurr;
|
inPrev = inCurr;
|
||||||
inCurr = inNext;
|
inCurr = inNext;
|
||||||
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
|
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
|
||||||
@@ -89,24 +95,117 @@ class TransportCryptoImpl implements TransportCrypto {
|
|||||||
outCurr);
|
outCurr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
|
private SecretKey rotateKey(SecretKey k, long timePeriod) {
|
||||||
byte[] period = new byte[INT_64_BYTES];
|
byte[] period = new byte[INT_64_BYTES];
|
||||||
ByteUtils.writeUint64(rotationPeriod, period, 0);
|
writeUint64(timePeriod, period, 0);
|
||||||
return crypto.deriveKey(ROTATE_LABEL, k, period);
|
return crypto.deriveKey(ROTATE_LABEL, k, period);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey deriveTagKey(SecretKey master, TransportId t,
|
private SecretKey deriveTagKey(SecretKey rootKey, TransportId t,
|
||||||
boolean alice) {
|
boolean keyBelongsToAlice) {
|
||||||
String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
|
String label = keyBelongsToAlice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
|
||||||
byte[] id = StringUtils.toUtf8(t.getString());
|
byte[] id = toUtf8(t.getString());
|
||||||
return crypto.deriveKey(label, master, id);
|
return crypto.deriveKey(label, rootKey, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
|
private SecretKey deriveHeaderKey(SecretKey rootKey, TransportId t,
|
||||||
boolean alice) {
|
boolean keyBelongsToAlice) {
|
||||||
String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL;
|
String label = keyBelongsToAlice ? ALICE_HEADER_LABEL :
|
||||||
byte[] id = StringUtils.toUtf8(t.getString());
|
BOB_HEADER_LABEL;
|
||||||
return crypto.deriveKey(label, master, id);
|
byte[] id = toUtf8(t.getString());
|
||||||
|
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
|
||||||
@@ -125,14 +224,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];
|
||||||
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
|
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];
|
||||||
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
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
|
||||||
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
arraycopy(mac, 0, tag, 0, TAG_LENGTH);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ 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;
|
||||||
@@ -23,8 +27,11 @@ 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.KeySet;
|
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
||||||
|
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;
|
||||||
@@ -33,11 +40,14 @@ import java.util.Map;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A low-level interface to the database (DatabaseComponent provides a
|
* A low-level interface to the database ({@link DatabaseComponent} provides a
|
||||||
* high-level interface). Most operations take a transaction argument, which is
|
* high-level interface).
|
||||||
* obtained by calling {@link #startTransaction()}. Every transaction must be
|
* <p/>
|
||||||
* terminated by calling either {@link #abortTransaction(Object) abortTransaction(T)} or
|
* Most operations take a transaction argument, which is obtained by calling
|
||||||
* {@link #commitTransaction(Object) commitTransaction(T)}, even if an exception is thrown.
|
* {@link #startTransaction()}. Every transaction must be terminated by calling
|
||||||
|
* 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> {
|
||||||
@@ -77,11 +87,10 @@ interface Database<T> {
|
|||||||
void commitTransaction(T txn) throws DbException;
|
void commitTransaction(T txn) throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a contact associated with the given local and remote pseudonyms,
|
* Stores a contact with the given pseudonym and returns an ID for the
|
||||||
* and returns an ID for the contact.
|
* contact.
|
||||||
*/
|
*/
|
||||||
ContactId addContact(T txn, Author remote, AuthorId local, boolean verified,
|
ContactId addContact(T txn, Author a, boolean verified) throws DbException;
|
||||||
boolean active) throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a group.
|
* Stores a group.
|
||||||
@@ -95,6 +104,20 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -121,6 +144,11 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -131,17 +159,15 @@ 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.
|
||||||
*/
|
*/
|
||||||
KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
|
TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given contact for the given
|
* Returns true if the database contains the given contact.
|
||||||
* local pseudonym.
|
|
||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
boolean containsContact(T txn, AuthorId remote, AuthorId local)
|
boolean containsContact(T txn, AuthorId a) throws DbException;
|
||||||
throws DbException;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if the database contains the given contact.
|
* Returns true if the database contains the given contact.
|
||||||
@@ -171,6 +197,14 @@ 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/>
|
||||||
@@ -215,6 +249,13 @@ 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/>
|
||||||
@@ -222,28 +263,6 @@ 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/>
|
||||||
@@ -284,6 +303,14 @@ 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/>
|
||||||
@@ -474,6 +501,13 @@ 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
|
||||||
@@ -496,13 +530,19 @@ interface Database<T> {
|
|||||||
* <p/>
|
* <p/>
|
||||||
* Read-only.
|
* Read-only.
|
||||||
*/
|
*/
|
||||||
Collection<KeySet> getTransportKeys(T txn, TransportId t)
|
Collection<TransportKeySet> 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, KeySetId k)
|
void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -571,6 +611,12 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -588,6 +634,11 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -596,7 +647,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, KeySetId k)
|
void removeTransportKeys(T txn, TransportId t, TransportKeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -610,12 +661,6 @@ 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.
|
||||||
*/
|
*/
|
||||||
@@ -641,16 +686,29 @@ interface Database<T> {
|
|||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the reordering window for the given key set and transport in the
|
* Sets the state of the given pending contact.
|
||||||
* given rotation period.
|
|
||||||
*/
|
*/
|
||||||
void setReorderingWindow(T txn, KeySetId k, TransportId t,
|
void setPendingContactState(T txn, PendingContactId p,
|
||||||
long rotationPeriod, long base, byte[] bitmap) throws DbException;
|
PendingContactState state) 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, KeySetId k)
|
void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k)
|
||||||
throws DbException;
|
throws DbException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -661,8 +719,13 @@ 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, KeySet ks) throws DbException;
|
void updateTransportKeys(T txn, TransportKeySet ks) throws DbException;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,27 +2,34 @@ 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.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;
|
||||||
@@ -55,8 +62,11 @@ 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.KeySet;
|
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
||||||
import org.briarproject.bramble.api.transport.KeySetId;
|
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
||||||
|
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;
|
||||||
@@ -64,6 +74,7 @@ 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;
|
||||||
@@ -92,25 +103,29 @@ 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 ShutdownManager shutdown;
|
private final Executor eventExecutor;
|
||||||
|
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,
|
||||||
ShutdownManager shutdown) {
|
@EventExecutor Executor eventExecutor,
|
||||||
|
ShutdownManager shutdownManager) {
|
||||||
this.db = db;
|
this.db = db;
|
||||||
this.txnClass = txnClass;
|
this.txnClass = txnClass;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
this.shutdown = shutdown;
|
this.eventExecutor = eventExecutor;
|
||||||
|
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);
|
||||||
shutdown.addShutdownHook(() -> {
|
shutdownManager.addShutdownHook(() -> {
|
||||||
try {
|
try {
|
||||||
close();
|
close();
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
@@ -160,13 +175,16 @@ 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()) db.abortTransaction(txn);
|
if (transaction.isCommitted()) {
|
||||||
|
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
|
||||||
@@ -214,20 +232,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ContactId addContact(Transaction transaction, Author remote,
|
public ContactId addContact(Transaction transaction, Author a,
|
||||||
AuthorId local, boolean verified, boolean active)
|
boolean verified) 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.containsLocalAuthor(txn, local))
|
if (db.containsLocalAuthor(txn, a.getId()))
|
||||||
throw new NoSuchLocalAuthorException();
|
|
||||||
if (db.containsLocalAuthor(txn, remote.getId()))
|
|
||||||
throw new ContactExistsException();
|
throw new ContactExistsException();
|
||||||
if (db.containsContact(txn, remote.getId(), local))
|
if (db.containsContact(txn, a.getId()))
|
||||||
throw new ContactExistsException();
|
throw new ContactExistsException();
|
||||||
ContactId c = db.addContact(txn, remote, local, verified, active);
|
ContactId c = db.addContact(txn, a, verified);
|
||||||
transaction.attach(new ContactAddedEvent(c, active));
|
transaction.attach(new ContactAddedEvent(c));
|
||||||
if (active) transaction.attach(new ContactStatusChangedEvent(c, true));
|
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -241,6 +255,30 @@ 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 {
|
||||||
@@ -269,6 +307,16 @@ 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 {
|
||||||
@@ -279,8 +327,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeySetId addTransportKeys(Transaction transaction, ContactId c,
|
public TransportKeySetId addTransportKeys(Transaction transaction,
|
||||||
TransportKeys k) throws DbException {
|
ContactId c, 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))
|
||||||
@@ -291,12 +339,10 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean containsContact(Transaction transaction, AuthorId remote,
|
public boolean containsContact(Transaction transaction, AuthorId a)
|
||||||
AuthorId local) throws DbException {
|
throws DbException {
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
if (!db.containsLocalAuthor(txn, local))
|
return db.containsContact(txn, a);
|
||||||
throw new NoSuchLocalAuthorException();
|
|
||||||
return db.containsContact(txn, remote, local);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -313,6 +359,13 @@ 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 {
|
||||||
@@ -429,6 +482,15 @@ 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 {
|
||||||
@@ -436,22 +498,6 @@ 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 {
|
||||||
@@ -486,6 +532,15 @@ 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 {
|
||||||
@@ -643,6 +698,13 @@ 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 {
|
||||||
@@ -651,7 +713,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Collection<KeySet> getTransportKeys(Transaction transaction,
|
public Collection<TransportKeySet> 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))
|
||||||
@@ -661,7 +723,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
public void incrementStreamCounter(Transaction transaction, TransportId t,
|
||||||
KeySetId k) throws DbException {
|
HandshakeKeySetId 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))
|
||||||
@@ -810,6 +882,16 @@ 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 {
|
||||||
@@ -832,6 +914,16 @@ 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 {
|
||||||
@@ -844,7 +936,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeTransportKeys(Transaction transaction,
|
public void removeTransportKeys(Transaction transaction,
|
||||||
TransportId t, KeySetId k) throws DbException {
|
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))
|
||||||
@@ -863,17 +955,6 @@ 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 {
|
||||||
@@ -943,19 +1024,30 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setReorderingWindow(Transaction transaction, KeySetId k,
|
public void setReorderingWindow(Transaction transaction,
|
||||||
TransportId t, long rotationPeriod, long base, byte[] bitmap)
|
TransportKeySetId k, TransportId t, long timePeriod, long base,
|
||||||
throws DbException {
|
byte[] bitmap) 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, rotationPeriod, base, bitmap);
|
db.setReorderingWindow(txn, k, t, timePeriod, 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,
|
||||||
KeySetId k) throws DbException {
|
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))
|
||||||
@@ -964,14 +1056,39 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateTransportKeys(Transaction transaction,
|
public void updateHandshakeKeys(Transaction transaction,
|
||||||
Collection<KeySet> keys) throws DbException {
|
Collection<HandshakeKeySet> keys) throws DbException {
|
||||||
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
T txn = unbox(transaction);
|
T txn = unbox(transaction);
|
||||||
for (KeySet ks : keys) {
|
for (HandshakeKeySet ks : keys) {
|
||||||
TransportId t = ks.getTransportKeys().getTransportId();
|
TransportId t = ks.getKeys().getTransportId();
|
||||||
|
if (db.containsTransport(txn, t))
|
||||||
|
db.updateHandshakeKeys(txn, ks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateTransportKeys(Transaction transaction,
|
||||||
|
Collection<TransportKeySet> keys) throws DbException {
|
||||||
|
if (transaction.isReadOnly()) throw new IllegalArgumentException();
|
||||||
|
T txn = unbox(transaction);
|
||||||
|
for (TransportKeySet ks : keys) {
|
||||||
|
TransportId t = ks.getKeys().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());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,13 @@ 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.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;
|
||||||
|
|
||||||
@@ -27,8 +29,9 @@ public class DatabaseModule {
|
|||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
DatabaseComponent provideDatabaseComponent(Database<Connection> db,
|
DatabaseComponent provideDatabaseComponent(Database<Connection> db,
|
||||||
EventBus eventBus, ShutdownManager shutdown) {
|
EventBus eventBus, @EventExecutor Executor eventExecutor,
|
||||||
|
ShutdownManager shutdownManager) {
|
||||||
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,30 +76,6 @@ 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;
|
||||||
|
|||||||
@@ -86,30 +86,6 @@ 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;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -17,7 +17,7 @@ class Migration40_41 implements Migration<Connection> {
|
|||||||
|
|
||||||
private final DatabaseTypes dbTypes;
|
private final DatabaseTypes dbTypes;
|
||||||
|
|
||||||
public Migration40_41(DatabaseTypes databaseTypes) {
|
Migration40_41(DatabaseTypes databaseTypes) {
|
||||||
this.dbTypes = databaseTypes;
|
this.dbTypes = databaseTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,97 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
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;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,13 +2,16 @@ 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
|
||||||
@@ -16,6 +19,12 @@ 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) {
|
||||||
@@ -29,6 +38,8 @@ class EventBusImpl implements EventBus {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void broadcast(Event e) {
|
public void broadcast(Event e) {
|
||||||
for (EventListener l : listeners) l.eventOccurred(e);
|
eventExecutor.execute(() -> {
|
||||||
|
for (EventListener l : listeners) l.eventOccurred(e);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ public class EventModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
EventBus provideEventBus() {
|
EventBus provideEventBus(EventBusImpl eventBus) {
|
||||||
return new EventBusImpl();
|
return eventBus;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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_SECRET_LABEL;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_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 secret.
|
* @return the negotiated master key.
|
||||||
* @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_SECRET_LABEL, s);
|
return crypto.deriveKey(MASTER_KEY_LABEL, s);
|
||||||
} catch (AbortException e) {
|
} catch (AbortException e) {
|
||||||
sendAbort(e.getCause() != null);
|
sendAbort(e.getCause() != null);
|
||||||
throw e;
|
throw e;
|
||||||
|
|||||||
@@ -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 master = protocol.perform();
|
SecretKey masterKey = protocol.perform();
|
||||||
KeyAgreementResult result =
|
KeyAgreementResult result =
|
||||||
new KeyAgreementResult(master, transport.getConnection(),
|
new KeyAgreementResult(masterKey, 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
|
||||||
|
|||||||
@@ -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.ContactStatusChangedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||||
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,12 +80,10 @@ class Poller implements EventListener {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (e instanceof ContactStatusChangedEvent) {
|
if (e instanceof ContactAddedEvent) {
|
||||||
ContactStatusChangedEvent c = (ContactStatusChangedEvent) e;
|
ContactAddedEvent c = (ContactAddedEvent) e;
|
||||||
if (c.isActive()) {
|
// Connect to the newly activated contact
|
||||||
// Connect to the newly activated contact
|
connectToContact(c.getContactId());
|
||||||
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
|
||||||
|
|||||||
@@ -17,16 +17,24 @@ public interface CircumventionProvider {
|
|||||||
String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"};
|
String[] BLOCKED = {"CN", "IR", "EG", "BY", "TR", "SY", "VE"};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Countries where vanilla bridge connection are likely to work.
|
* Countries where obfs4 bridge connection are likely to work.
|
||||||
* Should be a subset of {@link #BLOCKED}.
|
* Should be a subset of {@link #BLOCKED}.
|
||||||
*/
|
*/
|
||||||
String[] BRIDGES = { "EG", "BY", "TR", "SY", "VE" };
|
String[] BRIDGES = { "CN", "IR", "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();
|
List<String> getBridges(boolean meek);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ 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;
|
||||||
@@ -40,9 +42,14 @@ 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() {
|
public List<String> getBridges(boolean useMeek) {
|
||||||
List<String> bridges = this.bridges;
|
List<String> bridges = this.bridges;
|
||||||
if (bridges != null) return new ArrayList<>(bridges);
|
if (bridges != null) return new ArrayList<>(bridges);
|
||||||
|
|
||||||
@@ -53,6 +60,8 @@ 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();
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ 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;
|
||||||
@@ -469,13 +470,19 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
|
|||||||
if (!enable) callback.transportDisabled();
|
if (!enable) callback.transportDisabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableBridges(boolean enable) throws IOException {
|
private void enableBridges(boolean enable, boolean needsMeek)
|
||||||
|
throws IOException {
|
||||||
if (enable) {
|
if (enable) {
|
||||||
Collection<String> conf = new ArrayList<>();
|
Collection<String> conf = new ArrayList<>();
|
||||||
conf.add("UseBridges 1");
|
conf.add("UseBridges 1");
|
||||||
conf.add("ClientTransportPlugin obfs4 exec " +
|
if (needsMeek) {
|
||||||
obfs4File.getAbsolutePath());
|
conf.add("ClientTransportPlugin meek_lite exec " +
|
||||||
conf.addAll(circumventionProvider.getBridges());
|
obfs4File.getAbsolutePath());
|
||||||
|
} 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");
|
||||||
@@ -648,8 +655,10 @@ 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. Could be
|
// Works around a bug introduced in Tor 0.3.4.8.
|
||||||
// replaced with callback.transportDisabled() when fixed.
|
// https://trac.torproject.org/projects/tor/ticket/28027
|
||||||
|
// Could be replaced with callback.transportDisabled()
|
||||||
|
// when fixed.
|
||||||
disableNetwork();
|
disableNetwork();
|
||||||
updateConnectionStatus(networkManager.getNetworkStatus(),
|
updateConnectionStatus(networkManager.getNetworkStatus(),
|
||||||
batteryManager.isCharging());
|
batteryManager.isCharging());
|
||||||
@@ -685,6 +694,8 @@ 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;
|
||||||
|
|
||||||
@@ -699,21 +710,29 @@ 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 due to setting");
|
LOG.info("Disabling network, device is using mobile data");
|
||||||
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)) {
|
||||||
LOG.info("Enabling network, using bridges");
|
if (circumventionProvider.needsMeek(country)) {
|
||||||
enableBridges(true);
|
LOG.info("Enabling network, using meek bridges");
|
||||||
|
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);
|
enableBridges(false, false);
|
||||||
enableNetwork(true);
|
enableNetwork(true);
|
||||||
}
|
}
|
||||||
if (online && wifi && charging) {
|
if (online && wifi && charging) {
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ 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;
|
||||||
@@ -44,6 +46,7 @@ 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;
|
||||||
@@ -53,11 +56,13 @@ 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;
|
||||||
@@ -77,7 +82,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);
|
Group g = getContactGroup(c, getLocalAuthorId(txn));
|
||||||
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,
|
||||||
@@ -93,14 +98,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));
|
db.removeGroup(txn, getContactGroup(c, getLocalAuthorId(txn)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@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);
|
Group g = getContactGroup(c, getLocalAuthorId(txn));
|
||||||
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
|
db.setGroupVisibility(txn, c.getId(), g.getId(), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,7 +137,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));
|
Group g = getContactGroup(db.getContact(txn, c), getLocalAuthorId(txn));
|
||||||
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);
|
||||||
@@ -191,17 +196,16 @@ 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, t));
|
remote.put(c.getId(), getRemoteProperties(txn, c, local, t));
|
||||||
return remote;
|
return remote;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransportProperties getRemoteProperties(Transaction txn, Contact c,
|
private TransportProperties getRemoteProperties(Transaction txn, Contact c,
|
||||||
TransportId t) throws DbException {
|
AuthorId local, TransportId t) throws DbException {
|
||||||
// Don't return properties for inactive contacts
|
Group g = getContactGroup(c, local);
|
||||||
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);
|
||||||
@@ -218,8 +222,11 @@ 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 -> {
|
||||||
getRemoteProperties(txn, db.getContact(txn, c), t));
|
Contact contact = db.getContact(txn ,c);
|
||||||
|
AuthorId local = getLocalAuthorId(txn);
|
||||||
|
return getRemoteProperties(txn, contact, local, t);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -252,8 +259,9 @@ 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);
|
Group g = getContactGroup(c, localAuthorId);
|
||||||
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,
|
||||||
@@ -269,9 +277,13 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Group getContactGroup(Contact c) {
|
private AuthorId getLocalAuthorId(Transaction txn) throws DbException {
|
||||||
|
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);
|
MAJOR_VERSION, c, local);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void storeMessage(Transaction txn, GroupId g, TransportId t,
|
private void storeMessage(Transaction txn, GroupId g, TransportId t,
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
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;
|
||||||
@@ -19,8 +17,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;
|
||||||
@@ -46,7 +44,6 @@ 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);
|
||||||
|
|
||||||
@@ -58,8 +55,6 @@ 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<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,8 +68,6 @@ 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()) {
|
||||||
@@ -95,22 +88,22 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
|
public Map<TransportId, TransportKeySetId> addContact(Transaction txn,
|
||||||
SecretKey master, long timestamp, boolean alice, boolean active)
|
ContactId c, SecretKey rootKey, long timestamp, boolean alice,
|
||||||
throws DbException {
|
boolean active) throws DbException {
|
||||||
Map<TransportId, KeySetId> ids = new HashMap<>();
|
Map<TransportId, TransportKeySetId> 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, master, timestamp, alice, active));
|
ids.put(t, m.addContact(txn, c, rootKey, timestamp, alice, active));
|
||||||
}
|
}
|
||||||
return ids;
|
return ids;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activateKeys(Transaction txn, Map<TransportId, KeySetId> keys)
|
public void activateKeys(Transaction txn, Map<TransportId,
|
||||||
throws DbException {
|
TransportKeySetId> keys) throws DbException {
|
||||||
for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
|
for (Entry<TransportId, TransportKeySetId> 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) {
|
||||||
@@ -130,8 +123,6 @@ 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);
|
||||||
@@ -157,15 +148,10 @@ 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);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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 rotationPeriod;
|
private final long timePeriod;
|
||||||
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();
|
||||||
rotationPeriod = in.getRotationPeriod();
|
timePeriod = in.getTimePeriod();
|
||||||
window = new ReorderingWindow(in.getWindowBase(), in.getWindowBitmap());
|
window = new ReorderingWindow(in.getWindowBase(), in.getWindowBitmap());
|
||||||
}
|
}
|
||||||
|
|
||||||
IncomingKeys snapshot() {
|
IncomingKeys snapshot() {
|
||||||
return new IncomingKeys(tagKey, headerKey, rotationPeriod,
|
return new IncomingKeys(tagKey, headerKey, timePeriod,
|
||||||
window.getBase(), window.getBitmap());
|
window.getBase(), window.getBitmap());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,8 +34,8 @@ class MutableIncomingKeys {
|
|||||||
return headerKey;
|
return headerKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getRotationPeriod() {
|
long getTimePeriod() {
|
||||||
return rotationPeriod;
|
return timePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReorderingWindow getWindow() {
|
ReorderingWindow getWindow() {
|
||||||
|
|||||||
@@ -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.KeySetId;
|
import org.briarproject.bramble.api.transport.TransportKeySetId;
|
||||||
|
|
||||||
class MutableKeySet {
|
class MutableKeySet {
|
||||||
|
|
||||||
private final KeySetId keySetId;
|
private final TransportKeySetId keySetId;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final MutableTransportKeys transportKeys;
|
private final MutableTransportKeys transportKeys;
|
||||||
|
|
||||||
MutableKeySet(KeySetId keySetId, ContactId contactId,
|
MutableKeySet(TransportKeySetId 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeySetId getKeySetId() {
|
TransportKeySetId getKeySetId() {
|
||||||
return keySetId;
|
return keySetId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 rotationPeriod;
|
private final long timePeriod;
|
||||||
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();
|
||||||
rotationPeriod = out.getRotationPeriod();
|
timePeriod = out.getTimePeriod();
|
||||||
streamCounter = out.getStreamCounter();
|
streamCounter = out.getStreamCounter();
|
||||||
active = out.isActive();
|
active = out.isActive();
|
||||||
}
|
}
|
||||||
|
|
||||||
OutgoingKeys snapshot() {
|
OutgoingKeys snapshot() {
|
||||||
return new OutgoingKeys(tagKey, headerKey, rotationPeriod,
|
return new OutgoingKeys(tagKey, headerKey, timePeriod,
|
||||||
streamCounter, active);
|
streamCounter, active);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -36,8 +36,8 @@ class MutableOutgoingKeys {
|
|||||||
return headerKey;
|
return headerKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getRotationPeriod() {
|
long getTimePeriod() {
|
||||||
return rotationPeriod;
|
return timePeriod;
|
||||||
}
|
}
|
||||||
|
|
||||||
long getStreamCounter() {
|
long getStreamCounter() {
|
||||||
|
|||||||
@@ -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,10 +15,11 @@ interface TransportKeyManager {
|
|||||||
|
|
||||||
void start(Transaction txn) throws DbException;
|
void start(Transaction txn) throws DbException;
|
||||||
|
|
||||||
KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
|
TransportKeySetId addContact(Transaction txn, ContactId c,
|
||||||
long timestamp, boolean alice, boolean active) throws DbException;
|
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||||
|
throws DbException;
|
||||||
|
|
||||||
void activateKeys(Transaction txn, KeySetId k) throws DbException;
|
void activateKeys(Transaction txn, TransportKeySetId k) throws DbException;
|
||||||
|
|
||||||
void removeContact(ContactId c);
|
void removeContact(ContactId c);
|
||||||
|
|
||||||
|
|||||||
@@ -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 rotationPeriodLength;
|
private final long timePeriodLength;
|
||||||
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<KeySetId, MutableKeySet> keys = new HashMap<>();
|
private final Map<TransportKeySetId, 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;
|
||||||
rotationPeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
|
timePeriodLength = maxLatency + MAX_CLOCK_DIFFERENCE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -80,8 +80,9 @@ 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<KeySet> loaded = db.getTransportKeys(txn, transportId);
|
Collection<TransportKeySet> loaded =
|
||||||
// Rotate the keys to the current rotation period
|
db.getTransportKeys(txn, transportId);
|
||||||
|
// 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);
|
||||||
@@ -95,15 +96,17 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
scheduleKeyRotation(now);
|
scheduleKeyRotation(now);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RotationResult rotateKeys(Collection<KeySet> keys, long now) {
|
private RotationResult rotateKeys(Collection<TransportKeySet> keys,
|
||||||
|
long now) {
|
||||||
RotationResult rotationResult = new RotationResult();
|
RotationResult rotationResult = new RotationResult();
|
||||||
long rotationPeriod = now / rotationPeriodLength;
|
long timePeriod = now / timePeriodLength;
|
||||||
for (KeySet ks : keys) {
|
for (TransportKeySet ks : keys) {
|
||||||
TransportKeys k = ks.getTransportKeys();
|
TransportKeys k = ks.getKeys();
|
||||||
TransportKeys k1 =
|
TransportKeys k1 = transportCrypto.rotateTransportKeys(k,
|
||||||
transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
timePeriod);
|
||||||
KeySet ks1 = new KeySet(ks.getKeySetId(), ks.getContactId(), k1);
|
TransportKeySet ks1 = new TransportKeySet(ks.getKeySetId(),
|
||||||
if (k1.getRotationPeriod() > k.getRotationPeriod())
|
ks.getContactId(), k1);
|
||||||
|
if (k1.getTimePeriod() > k.getTimePeriod())
|
||||||
rotationResult.rotated.add(ks1);
|
rotationResult.rotated.add(ks1);
|
||||||
rotationResult.current.add(ks1);
|
rotationResult.current.add(ks1);
|
||||||
}
|
}
|
||||||
@@ -111,15 +114,15 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Locking: lock
|
// Locking: lock
|
||||||
private void addKeys(Collection<KeySet> keys) {
|
private void addKeys(Collection<TransportKeySet> keys) {
|
||||||
for (KeySet ks : keys) {
|
for (TransportKeySet ks : keys) {
|
||||||
addKeys(ks.getKeySetId(), ks.getContactId(),
|
addKeys(ks.getKeySetId(), ks.getContactId(),
|
||||||
new MutableTransportKeys(ks.getTransportKeys()));
|
new MutableTransportKeys(ks.getKeys()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Locking: lock
|
// Locking: lock
|
||||||
private void addKeys(KeySetId keySetId, ContactId contactId,
|
private void addKeys(TransportKeySetId 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);
|
||||||
@@ -130,7 +133,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Locking: lock
|
// Locking: lock
|
||||||
private void encodeTags(KeySetId keySetId, ContactId contactId,
|
private void encodeTags(TransportKeySetId keySetId, ContactId contactId,
|
||||||
MutableIncomingKeys inKeys) {
|
MutableIncomingKeys inKeys) {
|
||||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||||
TagContext tagCtx =
|
TagContext tagCtx =
|
||||||
@@ -155,7 +158,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleKeyRotation(long now) {
|
private void scheduleKeyRotation(long now) {
|
||||||
long delay = rotationPeriodLength - now % rotationPeriodLength;
|
long delay = timePeriodLength - now % timePeriodLength;
|
||||||
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
|
scheduler.schedule((Runnable) this::rotateKeys, delay, MILLISECONDS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -170,20 +173,21 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeySetId addContact(Transaction txn, ContactId c, SecretKey master,
|
public TransportKeySetId addContact(Transaction txn, ContactId c,
|
||||||
long timestamp, boolean alice, boolean active) throws DbException {
|
SecretKey rootKey, long timestamp, boolean alice, boolean active)
|
||||||
|
throws DbException {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
// Work out what rotation period the timestamp belongs to
|
// Work out what time period the timestamp belongs to
|
||||||
long rotationPeriod = timestamp / rotationPeriodLength;
|
long timePeriod = timestamp / timePeriodLength;
|
||||||
// Derive the transport keys
|
// Derive the transport keys
|
||||||
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
TransportKeys k = transportCrypto.deriveTransportKeys(transportId,
|
||||||
master, rotationPeriod, alice, active);
|
rootKey, timePeriod, alice, active);
|
||||||
// Rotate the keys to the current rotation period if necessary
|
// Rotate the keys to the current time period if necessary
|
||||||
rotationPeriod = clock.currentTimeMillis() / rotationPeriodLength;
|
timePeriod = clock.currentTimeMillis() / timePeriodLength;
|
||||||
k = transportCrypto.rotateTransportKeys(k, rotationPeriod);
|
k = transportCrypto.rotateTransportKeys(k, timePeriod);
|
||||||
// Write the keys back to the DB
|
// Write the keys back to the DB
|
||||||
KeySetId keySetId = db.addTransportKeys(txn, c, k);
|
TransportKeySetId 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;
|
||||||
@@ -193,7 +197,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void activateKeys(Transaction txn, KeySetId k) throws DbException {
|
public void activateKeys(Transaction txn, TransportKeySetId k)
|
||||||
|
throws DbException {
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try {
|
try {
|
||||||
MutableKeySet ks = keys.get(k);
|
MutableKeySet ks = keys.get(k);
|
||||||
@@ -300,7 +305,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.getRotationPeriod(), window.getBase(),
|
inKeys.getTimePeriod(), 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);
|
||||||
@@ -322,11 +327,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 rotation period
|
// Rotate the keys to the current time period
|
||||||
Collection<KeySet> snapshot = new ArrayList<>(keys.size());
|
Collection<TransportKeySet> snapshot = new ArrayList<>(keys.size());
|
||||||
for (MutableKeySet ks : keys.values()) {
|
for (MutableKeySet ks : keys.values()) {
|
||||||
snapshot.add(new KeySet(ks.getKeySetId(), ks.getContactId(),
|
snapshot.add(new TransportKeySet(ks.getKeySetId(),
|
||||||
ks.getTransportKeys().snapshot()));
|
ks.getContactId(), 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
|
||||||
@@ -346,12 +351,12 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
|
|
||||||
private static class TagContext {
|
private static class TagContext {
|
||||||
|
|
||||||
private final KeySetId keySetId;
|
private final TransportKeySetId 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(KeySetId keySetId, ContactId contactId,
|
private TagContext(TransportKeySetId keySetId, ContactId contactId,
|
||||||
MutableIncomingKeys inKeys, long streamNumber) {
|
MutableIncomingKeys inKeys, long streamNumber) {
|
||||||
this.keySetId = keySetId;
|
this.keySetId = keySetId;
|
||||||
this.contactId = contactId;
|
this.contactId = contactId;
|
||||||
@@ -362,7 +367,7 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
|
|
||||||
private static class RotationResult {
|
private static class RotationResult {
|
||||||
|
|
||||||
private final Collection<KeySet> current = new ArrayList<>();
|
private final Collection<TransportKeySet> current = new ArrayList<>();
|
||||||
private final Collection<KeySet> rotated = new ArrayList<>();
|
private final Collection<TransportKeySet> rotated = new ArrayList<>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ 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;
|
||||||
@@ -58,6 +60,7 @@ 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;
|
||||||
@@ -68,9 +71,11 @@ 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,
|
||||||
@@ -89,14 +94,9 @@ 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 {
|
||||||
Contact contact = db.getContact(txn, contactId);
|
LatestUpdates latest = findLatestUpdates(txn, contactId);
|
||||||
Group g = getContactGroup(contact);
|
if (latest == null || latest.remote == null) return INVISIBLE;
|
||||||
// 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,6 +110,24 @@ 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;
|
||||||
@@ -141,7 +159,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);
|
Group g = getContactGroup(c, getLocalAuthorId(txn));
|
||||||
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
|
||||||
@@ -160,7 +178,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));
|
db.removeGroup(txn, getContactGroup(c, getLocalAuthorId(txn)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -295,7 +313,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);
|
Group g = getContactGroup(c, getLocalAuthorId(txn));
|
||||||
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();
|
||||||
@@ -331,9 +349,24 @@ class ClientVersioningManagerImpl implements ClientVersioningManager, Client,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Group getContactGroup(Contact c) {
|
private AuthorId getLocalAuthorId(Transaction txn) throws DbException {
|
||||||
|
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);
|
MAJOR_VERSION, c, local);
|
||||||
|
}
|
||||||
|
|
||||||
|
@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)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
Bridge obfs4 78.46.188.239:37356 5A2D2F4158D0453E00C7C176978D3F41D69C45DB cert=3c0SwxpOisbohNxEc4tb875RVW8eOu1opRTVXJhafaKA/PNNtI7ElQIVOVZg1AdL5bxGCw iat-mode=0
|
Bridge obfs4 78.46.188.239:37356 5A2D2F4158D0453E00C7C176978D3F41D69C45DB cert=3c0SwxpOisbohNxEc4tb875RVW8eOu1opRTVXJhafaKA/PNNtI7ElQIVOVZg1AdL5bxGCw iat-mode=0
|
||||||
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
|
||||||
@@ -5,27 +5,20 @@ 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;
|
||||||
@@ -33,8 +26,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;
|
||||||
@@ -49,42 +42,40 @@ 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 ContactId contactId = new ContactId(42);
|
private final Author author = getAuthor();
|
||||||
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 =
|
private final Contact contact = getContact(author, verified);
|
||||||
new Contact(contactId, remote, local, alias, verified, active);
|
private final ContactId contactId = contact.getId();
|
||||||
|
|
||||||
public ContactManagerImplTest() {
|
public ContactManagerImplTest() {
|
||||||
contactManager = new ContactManagerImpl(db, keyManager, identityManager);
|
contactManager =
|
||||||
|
new ContactManagerImpl(db, keyManager, identityManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAddContact() throws Exception {
|
public void testAddContact() throws Exception {
|
||||||
SecretKey master = getSecretKey();
|
SecretKey rootKey = 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, remote, local, verified, active);
|
oneOf(db).addContact(txn, author, verified);
|
||||||
will(returnValue(contactId));
|
will(returnValue(contactId));
|
||||||
oneOf(keyManager).addContact(txn, contactId, master, timestamp,
|
oneOf(keyManager).addContact(txn, contactId, rootKey, 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(remote, local,
|
assertEquals(contactId, contactManager.addContact(author, rootKey,
|
||||||
master, timestamp, alice, verified, active));
|
timestamp, alice, verified, active));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetContact() throws Exception {
|
public void testGetContactByContactId() 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));
|
||||||
@@ -96,49 +87,20 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetContactByAuthor() throws Exception {
|
public void testGetContactByAuthorId() 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).getContactsByAuthorId(txn, remote.getId());
|
oneOf(db).getContact(txn, author.getId());
|
||||||
will(returnValue(emptyList()));
|
will(returnValue(contact));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
contactManager.getContact(remote.getId(), local);
|
assertEquals(contact, contactManager.getContact(author.getId()));
|
||||||
}
|
|
||||||
|
|
||||||
@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 testGetActiveContacts() throws Exception {
|
public void testGetContacts() throws Exception {
|
||||||
Collection<Contact> activeContacts = Collections.singletonList(contact);
|
Collection<Contact> contacts = 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));
|
||||||
@@ -146,7 +108,7 @@ public class ContactManagerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(contacts));
|
will(returnValue(contacts));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertEquals(activeContacts, contactManager.getActiveContacts());
|
assertEquals(contacts, contactManager.getContacts());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -162,19 +124,11 @@ 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);
|
||||||
@@ -195,85 +149,82 @@ 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, remote.getId(), local);
|
oneOf(db).containsContact(txn, author.getId());
|
||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
assertTrue(contactManager.contactExists(remote.getId(), local));
|
assertTrue(contactManager.contactExists(author.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetAuthorInfo() throws Exception {
|
public void testGetAuthorInfoOurselves() 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, remote.getId());
|
contactManager.getAuthorInfo(txn, localAuthor.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());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkAuthorInfoContext(Transaction txn, AuthorId authorId,
|
@Test
|
||||||
Collection<Contact> contacts) throws DbException {
|
public void testGetAuthorInfoVerified() throws Exception {
|
||||||
context.checking(new Expectations() {{
|
Transaction txn = new Transaction(null, true);
|
||||||
|
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).getContactsByAuthorId(txn, authorId);
|
oneOf(db).containsContact(txn, author.getId());
|
||||||
will(returnValue(contacts));
|
will(returnValue(true));
|
||||||
|
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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,167 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
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())));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,167 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,7 +13,9 @@ 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;
|
||||||
@@ -43,18 +45,21 @@ 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;
|
||||||
@@ -70,13 +75,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;
|
||||||
@@ -87,9 +92,10 @@ 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 shutdown =
|
private final ShutdownManager shutdownManager =
|
||||||
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();
|
||||||
@@ -107,7 +113,8 @@ 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 KeySetId keySetId;
|
private final TransportKeySetId keySetId;
|
||||||
|
private final PendingContactId pendingContactId;
|
||||||
|
|
||||||
public DatabaseComponentImplTest() {
|
public DatabaseComponentImplTest() {
|
||||||
clientId = getClientId();
|
clientId = getClientId();
|
||||||
@@ -116,7 +123,6 @@ 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();
|
||||||
@@ -125,16 +131,18 @@ 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;
|
||||||
contactId = new ContactId(234);
|
contact = getContact(author, true);
|
||||||
contact = new Contact(contactId, author, localAuthor.getId(), alias,
|
contactId = contact.getId();
|
||||||
true, true);
|
alias = contact.getAlias();
|
||||||
keySetId = new KeySetId(345);
|
keySetId = new TransportKeySetId(345);
|
||||||
|
pendingContactId = new PendingContactId(getRandomId());
|
||||||
}
|
}
|
||||||
|
|
||||||
private DatabaseComponent createDatabaseComponent(Database<Object> database,
|
private DatabaseComponent createDatabaseComponent(Database<Object> database,
|
||||||
EventBus eventBus, ShutdownManager shutdown) {
|
EventBus eventBus, Executor eventExecutor,
|
||||||
|
ShutdownManager shutdownManager) {
|
||||||
return new DatabaseComponentImpl<>(database, Object.class, eventBus,
|
return new DatabaseComponentImpl<>(database, Object.class, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -144,7 +152,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(shutdown).addShutdownHook(with(any(Runnable.class)));
|
oneOf(shutdownManager).addShutdownHook(with(any(Runnable.class)));
|
||||||
will(returnValue(shutdownHandle));
|
will(returnValue(shutdownHandle));
|
||||||
// startTransaction()
|
// startTransaction()
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -155,19 +163,13 @@ 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, localAuthor.getId(),
|
oneOf(database).addContact(txn, author, true);
|
||||||
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)));
|
||||||
@@ -207,13 +209,12 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).close();
|
oneOf(database).close();
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
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,
|
assertEquals(contactId, db.addContact(transaction, author, true));
|
||||||
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
|
||||||
@@ -238,7 +239,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).abortTransaction(txn);
|
oneOf(database).abortTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.addLocalMessage(transaction, message, metadata, true));
|
db.addLocalMessage(transaction, message, metadata, true));
|
||||||
@@ -263,7 +264,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.addLocalMessage(transaction, message, metadata, true));
|
db.addLocalMessage(transaction, message, metadata, true));
|
||||||
@@ -274,14 +275,25 @@ 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(17).of(database).startTransaction();
|
exactly(18).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));
|
||||||
exactly(17).of(database).abortTransaction(txn);
|
oneOf(database).containsContact(txn, author.getId());
|
||||||
|
will(returnValue(false));
|
||||||
|
exactly(18).of(database).abortTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
|
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 ->
|
||||||
@@ -332,6 +344,14 @@ 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));
|
||||||
@@ -399,14 +419,6 @@ 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));
|
||||||
@@ -430,24 +442,15 @@ 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(3).of(database).startTransaction();
|
exactly(2).of(database).startTransaction();
|
||||||
will(returnValue(txn));
|
will(returnValue(txn));
|
||||||
exactly(3).of(database).containsLocalAuthor(txn,
|
exactly(2).of(database).containsLocalAuthor(txn,
|
||||||
localAuthor.getId());
|
localAuthor.getId());
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
exactly(3).of(database).abortTransaction(txn);
|
exactly(2).of(database).abortTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
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 ->
|
||||||
@@ -481,7 +484,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
@@ -565,7 +568,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(true));
|
will(returnValue(true));
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
@@ -668,7 +671,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
@@ -713,6 +716,39 @@ 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);
|
||||||
@@ -727,7 +763,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction -> {
|
db.transaction(false, transaction -> {
|
||||||
Ack a = db.generateAck(transaction, contactId, 123);
|
Ack a = db.generateAck(transaction, contactId, 123);
|
||||||
@@ -761,7 +797,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
assertEquals(messages, db.generateBatch(transaction, contactId,
|
assertEquals(messages, db.generateBatch(transaction, contactId,
|
||||||
@@ -786,7 +822,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction -> {
|
db.transaction(false, transaction -> {
|
||||||
Offer o = db.generateOffer(transaction, contactId, 123, maxLatency);
|
Offer o = db.generateOffer(transaction, contactId, 123, maxLatency);
|
||||||
@@ -810,7 +846,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction -> {
|
db.transaction(false, transaction -> {
|
||||||
Request r = db.generateRequest(transaction, contactId, 123);
|
Request r = db.generateRequest(transaction, contactId, 123);
|
||||||
@@ -844,7 +880,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
assertEquals(messages, db.generateRequestedBatch(transaction,
|
assertEquals(messages, db.generateRequestedBatch(transaction,
|
||||||
@@ -865,7 +901,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction -> {
|
db.transaction(false, transaction -> {
|
||||||
Ack a = new Ack(singletonList(messageId));
|
Ack a = new Ack(singletonList(messageId));
|
||||||
@@ -903,7 +939,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction -> {
|
db.transaction(false, transaction -> {
|
||||||
// Receive the message twice
|
// Receive the message twice
|
||||||
@@ -931,7 +967,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.receiveMessage(transaction, contactId, message));
|
db.receiveMessage(transaction, contactId, message));
|
||||||
@@ -949,7 +985,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.receiveMessage(transaction, contactId, message));
|
db.receiveMessage(transaction, contactId, message));
|
||||||
@@ -989,7 +1025,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
Offer o = new Offer(asList(messageId, messageId1,
|
Offer o = new Offer(asList(messageId, messageId1,
|
||||||
messageId2, messageId3));
|
messageId2, messageId3));
|
||||||
@@ -1012,7 +1048,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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
Request r = new Request(singletonList(messageId));
|
Request r = new Request(singletonList(messageId));
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
@@ -1042,7 +1078,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
GroupVisibilityUpdatedEvent.class, 0));
|
GroupVisibilityUpdatedEvent.class, 0));
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.setGroupVisibility(transaction, contactId, groupId,
|
db.setGroupVisibility(transaction, contactId, groupId,
|
||||||
@@ -1076,7 +1112,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
GroupVisibilityUpdatedEvent.class, 0));
|
GroupVisibilityUpdatedEvent.class, 0));
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.setGroupVisibility(transaction, contactId, groupId,
|
db.setGroupVisibility(transaction, contactId, groupId,
|
||||||
@@ -1102,7 +1138,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.setGroupVisibility(transaction, contactId, groupId,
|
db.setGroupVisibility(transaction, contactId, groupId,
|
||||||
@@ -1112,8 +1148,9 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testTransportKeys() throws Exception {
|
public void testTransportKeys() throws Exception {
|
||||||
TransportKeys transportKeys = createTransportKeys();
|
TransportKeys transportKeys = createTransportKeys();
|
||||||
KeySet ks = new KeySet(keySetId, contactId, transportKeys);
|
TransportKeySet ks =
|
||||||
Collection<KeySet> keys = singletonList(ks);
|
new TransportKeySet(keySetId, contactId, transportKeys);
|
||||||
|
Collection<TransportKeySet> keys = singletonList(ks);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// startTransaction()
|
// startTransaction()
|
||||||
@@ -1132,7 +1169,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction -> {
|
db.transaction(false, transaction -> {
|
||||||
db.updateTransportKeys(transaction, keys);
|
db.updateTransportKeys(transaction, keys);
|
||||||
@@ -1171,7 +1208,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(true, transaction -> {
|
db.transaction(true, transaction -> {
|
||||||
// With visible group - return stored status
|
// With visible group - return stored status
|
||||||
@@ -1221,7 +1258,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).commitTransaction(txn);
|
oneOf(database).commitTransaction(txn);
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(true, transaction -> {
|
db.transaction(true, transaction -> {
|
||||||
// With visible group - return stored status
|
// With visible group - return stored status
|
||||||
@@ -1240,6 +1277,27 @@ 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();
|
||||||
@@ -1287,7 +1345,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
db.transaction(false, transaction -> {
|
db.transaction(false, transaction -> {
|
||||||
// First merge should broadcast an event
|
// First merge should broadcast an event
|
||||||
@@ -1330,7 +1388,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
assertNotNull(db.startTransaction(firstTxnReadOnly));
|
assertNotNull(db.startTransaction(firstTxnReadOnly));
|
||||||
db.startTransaction(secondTxnReadOnly);
|
db.startTransaction(secondTxnReadOnly);
|
||||||
@@ -1342,8 +1400,6 @@ 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));
|
||||||
@@ -1351,12 +1407,11 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
}});
|
}});
|
||||||
|
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.addContact(transaction, author, localAuthor.getId(),
|
db.addContact(transaction, author, true));
|
||||||
true, true));
|
|
||||||
fail();
|
fail();
|
||||||
} catch (ContactExistsException expected) {
|
} catch (ContactExistsException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
@@ -1368,24 +1423,20 @@ 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 for this local identity
|
// Contact already exists
|
||||||
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,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
db.transaction(false, transaction ->
|
db.transaction(false, transaction ->
|
||||||
db.addContact(transaction, author, localAuthor.getId(),
|
db.addContact(transaction, author, true));
|
||||||
true, true));
|
|
||||||
fail();
|
fail();
|
||||||
} catch (ContactExistsException expected) {
|
} catch (ContactExistsException expected) {
|
||||||
// Expected
|
// Expected
|
||||||
@@ -1401,7 +1452,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(shutdown).addShutdownHook(with(any(Runnable.class)));
|
oneOf(shutdownManager).addShutdownHook(with(any(Runnable.class)));
|
||||||
will(returnValue(shutdownHandle));
|
will(returnValue(shutdownHandle));
|
||||||
// startTransaction()
|
// startTransaction()
|
||||||
oneOf(database).startTransaction();
|
oneOf(database).startTransaction();
|
||||||
@@ -1441,7 +1492,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
|
|||||||
oneOf(database).close();
|
oneOf(database).close();
|
||||||
}});
|
}});
|
||||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||||
shutdown);
|
eventExecutor, shutdownManager);
|
||||||
|
|
||||||
assertFalse(db.open(key, null));
|
assertFalse(db.open(key, null));
|
||||||
db.transaction(false, transaction -> {
|
db.transaction(false, transaction -> {
|
||||||
@@ -1455,4 +1506,43 @@ 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 {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -45,8 +45,7 @@ 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 =
|
protected final DatabaseConfig config = new TestDatabaseConfig(testDir);
|
||||||
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();
|
||||||
|
|||||||
@@ -76,8 +76,8 @@ public abstract class DatabasePerformanceComparisonTest
|
|||||||
private Database<Connection> openDatabase(boolean conditionA)
|
private Database<Connection> openDatabase(boolean conditionA)
|
||||||
throws DbException {
|
throws DbException {
|
||||||
Database<Connection> db = createDatabase(conditionA,
|
Database<Connection> db = createDatabase(conditionA,
|
||||||
new TestDatabaseConfig(testDir, MAX_SIZE),
|
new TestDatabaseConfig(testDir), new TestMessageFactory(),
|
||||||
new TestMessageFactory(), new SystemClock());
|
new SystemClock());
|
||||||
db.open(databaseKey, null);
|
db.open(databaseKey, null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ 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.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.identity.AuthorId;
|
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
import org.briarproject.bramble.api.sync.ClientId;
|
import org.briarproject.bramble.api.sync.ClientId;
|
||||||
import org.briarproject.bramble.api.sync.Group;
|
import org.briarproject.bramble.api.sync.Group;
|
||||||
@@ -49,9 +48,6 @@ import static org.junit.Assert.assertTrue;
|
|||||||
|
|
||||||
public abstract class DatabasePerformanceTest extends BrambleTestCase {
|
public abstract class DatabasePerformanceTest extends BrambleTestCase {
|
||||||
|
|
||||||
private static final int ONE_MEGABYTE = 1024 * 1024;
|
|
||||||
static final int MAX_SIZE = 100 * ONE_MEGABYTE;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* How many contacts to simulate.
|
* How many contacts to simulate.
|
||||||
*/
|
*/
|
||||||
@@ -134,11 +130,10 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testContainsContactByAuthorId() throws Exception {
|
public void testContainsContactByAuthorId() throws Exception {
|
||||||
String name = "containsContact(T, AuthorId, AuthorId)";
|
String name = "containsContact(T, AuthorId)";
|
||||||
benchmark(name, db -> {
|
benchmark(name, db -> {
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
AuthorId remote = pickRandom(contacts).getAuthor().getId();
|
db.containsContact(txn, pickRandom(contacts).getAuthor().getId());
|
||||||
db.containsContact(txn, remote, localAuthor.getId());
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -205,7 +200,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetContact() throws Exception {
|
public void testGetContactByContactId() throws Exception {
|
||||||
String name = "getContact(T, ContactId)";
|
String name = "getContact(T, ContactId)";
|
||||||
benchmark(name, db -> {
|
benchmark(name, db -> {
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
@@ -214,6 +209,16 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetContactByAuthorId() throws Exception {
|
||||||
|
String name = "getContact(T, AuthorId)";
|
||||||
|
benchmark(name, db -> {
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
db.getContact(txn, pickRandom(contacts).getAuthor().getId());
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetContacts() throws Exception {
|
public void testGetContacts() throws Exception {
|
||||||
String name = "getContacts(T)";
|
String name = "getContacts(T)";
|
||||||
@@ -224,27 +229,6 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetContactsByRemoteAuthorId() throws Exception {
|
|
||||||
String name = "getContactsByAuthorId(T, AuthorId)";
|
|
||||||
benchmark(name, db -> {
|
|
||||||
Connection txn = db.startTransaction();
|
|
||||||
AuthorId remote = pickRandom(contacts).getAuthor().getId();
|
|
||||||
db.getContactsByAuthorId(txn, remote);
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetContactsByLocalAuthorId() throws Exception {
|
|
||||||
String name = "getContacts(T, AuthorId)";
|
|
||||||
benchmark(name, db -> {
|
|
||||||
Connection txn = db.startTransaction();
|
|
||||||
db.getContacts(txn, localAuthor.getId());
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetGroup() throws Exception {
|
public void testGetGroup() throws Exception {
|
||||||
String name = "getGroup(T, GroupId)";
|
String name = "getGroup(T, GroupId)";
|
||||||
@@ -548,8 +532,7 @@ public abstract class DatabasePerformanceTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
db.addLocalAuthor(txn, localAuthor);
|
||||||
for (int i = 0; i < CONTACTS; i++) {
|
for (int i = 0; i < CONTACTS; i++) {
|
||||||
ContactId c = db.addContact(txn, getAuthor(), localAuthor.getId(),
|
ContactId c = db.addContact(txn, getAuthor(), random.nextBoolean());
|
||||||
random.nextBoolean(), true);
|
|
||||||
contacts.add(db.getContact(txn, c));
|
contacts.add(db.getContact(txn, c));
|
||||||
contactGroups.put(c, new ArrayList<>());
|
contactGroups.put(c, new ArrayList<>());
|
||||||
for (int j = 0; j < GROUPS_PER_CONTACT; j++) {
|
for (int j = 0; j < GROUPS_PER_CONTACT; j++) {
|
||||||
|
|||||||
@@ -48,8 +48,8 @@ public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
|
|||||||
|
|
||||||
private Database<Connection> openDatabase() throws DbException {
|
private Database<Connection> openDatabase() throws DbException {
|
||||||
Database<Connection> db = createDatabase(
|
Database<Connection> db = createDatabase(
|
||||||
new TestDatabaseConfig(testDir, MAX_SIZE),
|
new TestDatabaseConfig(testDir), new TestMessageFactory(),
|
||||||
new TestMessageFactory(), new SystemClock());
|
new SystemClock());
|
||||||
db.open(databaseKey, null);
|
db.open(databaseKey, null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ 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.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -20,10 +21,13 @@ 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.system.Clock;
|
import org.briarproject.bramble.api.system.Clock;
|
||||||
|
import org.briarproject.bramble.api.transport.HandshakeKeySet;
|
||||||
|
import org.briarproject.bramble.api.transport.HandshakeKeySetId;
|
||||||
|
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.system.SystemClock;
|
import org.briarproject.bramble.system.SystemClock;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
@@ -52,12 +56,12 @@ import static java.util.Collections.emptyMap;
|
|||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||||
|
import static org.briarproject.bramble.api.contact.PendingContactState.FAILED;
|
||||||
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
import static org.briarproject.bramble.api.db.Metadata.REMOVE;
|
||||||
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.sync.Group.Visibility.INVISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.INVISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.VISIBLE;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_BODY_LENGTH;
|
|
||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.DELIVERED;
|
||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.INVALID;
|
||||||
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
import static org.briarproject.bramble.api.sync.validation.MessageState.PENDING;
|
||||||
@@ -71,6 +75,7 @@ import static org.briarproject.bramble.test.TestUtils.getClientId;
|
|||||||
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.getPendingContact;
|
||||||
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.getTestDirectory;
|
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||||
@@ -79,7 +84,6 @@ import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
|||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
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.assertNotEquals;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -88,11 +92,9 @@ import static org.junit.Assert.fail;
|
|||||||
public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
||||||
|
|
||||||
private static final int ONE_MEGABYTE = 1024 * 1024;
|
private static final int ONE_MEGABYTE = 1024 * 1024;
|
||||||
private static final int MAX_SIZE = 5 * ONE_MEGABYTE;
|
|
||||||
// All our transports use a maximum latency of 30 seconds
|
// All our transports use a maximum latency of 30 seconds
|
||||||
private static final int MAX_LATENCY = 30 * 1000;
|
private static final int MAX_LATENCY = 30 * 1000;
|
||||||
|
|
||||||
|
|
||||||
private final SecretKey key = getSecretKey();
|
private final SecretKey key = getSecretKey();
|
||||||
private final File testDir = getTestDirectory();
|
private final File testDir = getTestDirectory();
|
||||||
private final GroupId groupId;
|
private final GroupId groupId;
|
||||||
@@ -105,7 +107,9 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
private final MessageId messageId;
|
private final MessageId messageId;
|
||||||
private final TransportId transportId;
|
private final TransportId transportId;
|
||||||
private final ContactId contactId;
|
private final ContactId contactId;
|
||||||
private final KeySetId keySetId, keySetId1;
|
private final TransportKeySetId keySetId, keySetId1;
|
||||||
|
private final HandshakeKeySetId handshakeKeySetId, handshakeKeySetId1;
|
||||||
|
private final PendingContact pendingContact;
|
||||||
private final Random random = new Random();
|
private final Random random = new Random();
|
||||||
|
|
||||||
JdbcDatabaseTest() {
|
JdbcDatabaseTest() {
|
||||||
@@ -119,8 +123,11 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
messageId = message.getId();
|
messageId = message.getId();
|
||||||
transportId = getTransportId();
|
transportId = getTransportId();
|
||||||
contactId = new ContactId(1);
|
contactId = new ContactId(1);
|
||||||
keySetId = new KeySetId(1);
|
keySetId = new TransportKeySetId(1);
|
||||||
keySetId1 = new KeySetId(2);
|
keySetId1 = new TransportKeySetId(2);
|
||||||
|
handshakeKeySetId = new HandshakeKeySetId(1);
|
||||||
|
handshakeKeySetId1 = new HandshakeKeySetId(2);
|
||||||
|
pendingContact = getPendingContact();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract JdbcDatabase createDatabase(DatabaseConfig config,
|
protected abstract JdbcDatabase createDatabase(DatabaseConfig config,
|
||||||
@@ -138,8 +145,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
assertFalse(db.containsContact(txn, contactId));
|
assertFalse(db.containsContact(txn, contactId));
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
db.addLocalAuthor(txn, localAuthor);
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
true, true));
|
|
||||||
assertTrue(db.containsContact(txn, contactId));
|
assertTrue(db.containsContact(txn, contactId));
|
||||||
assertFalse(db.containsGroup(txn, groupId));
|
assertFalse(db.containsGroup(txn, groupId));
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
@@ -200,9 +206,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
@@ -231,9 +235,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared but unvalidated message
|
// Add a contact, a shared group and a shared but unvalidated message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, UNKNOWN, true, null);
|
db.addMessage(txn, message, UNKNOWN, true, null);
|
||||||
@@ -276,9 +278,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, an invisible group and a shared message
|
// Add a contact, an invisible group and a shared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
|
|
||||||
@@ -327,9 +327,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and an unshared message
|
// Add a contact, a shared group and an unshared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, DELIVERED, false, null);
|
db.addMessage(txn, message, DELIVERED, false, null);
|
||||||
@@ -358,17 +356,14 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
|
|
||||||
// The message is sendable, but too large to send
|
// The message is sendable, but too large to send
|
||||||
Collection<MessageId> ids =
|
Collection<MessageId> ids = db.getMessagesToSend(txn, contactId,
|
||||||
db.getMessagesToSend(txn, contactId, message.getRawLength() - 1,
|
message.getRawLength() - 1, MAX_LATENCY);
|
||||||
MAX_LATENCY);
|
|
||||||
assertTrue(ids.isEmpty());
|
assertTrue(ids.isEmpty());
|
||||||
// The message is just the right size to send
|
// The message is just the right size to send
|
||||||
ids = db.getMessagesToSend(txn, contactId, message.getRawLength(),
|
ids = db.getMessagesToSend(txn, contactId, message.getRawLength(),
|
||||||
@@ -385,9 +380,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact and a visible group
|
// Add a contact and a visible group
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, false);
|
db.addGroupVisibility(txn, contactId, groupId, false);
|
||||||
|
|
||||||
@@ -426,9 +419,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
@@ -454,29 +445,6 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testGetFreeSpace() throws Exception {
|
|
||||||
Message message = getMessage(groupId, MAX_MESSAGE_BODY_LENGTH);
|
|
||||||
Database<Connection> db = open(false);
|
|
||||||
|
|
||||||
// Sanity check: there should be enough space on disk for this test
|
|
||||||
assertTrue(testDir.getFreeSpace() > MAX_SIZE);
|
|
||||||
|
|
||||||
// The free space should not be more than the allowed maximum size
|
|
||||||
long free = db.getFreeSpace();
|
|
||||||
assertTrue(free <= MAX_SIZE);
|
|
||||||
assertTrue(free > 0);
|
|
||||||
|
|
||||||
// Storing a message should reduce the free space
|
|
||||||
Connection txn = db.startTransaction();
|
|
||||||
db.addGroup(txn, group);
|
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
assertTrue(db.getFreeSpace() < free);
|
|
||||||
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCloseWaitsForCommit() throws Exception {
|
public void testCloseWaitsForCommit() throws Exception {
|
||||||
CountDownLatch closing = new CountDownLatch(1);
|
CountDownLatch closing = new CountDownLatch(1);
|
||||||
@@ -581,9 +549,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact and a shared group
|
// Add a contact and a shared group
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
|
|
||||||
@@ -601,9 +567,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact
|
// Add a contact
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
|
|
||||||
// The group is not in the database
|
// The group is not in the database
|
||||||
assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
|
assertFalse(db.containsVisibleMessage(txn, contactId, messageId));
|
||||||
@@ -619,9 +583,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, an invisible group and a message
|
// Add a contact, an invisible group and a message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
|
|
||||||
@@ -638,9 +600,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact and a group
|
// Add a contact and a group
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
|
|
||||||
// The group should not be visible to the contact
|
// The group should not be visible to the contact
|
||||||
@@ -678,10 +638,10 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testTransportKeys() throws Exception {
|
public void testTransportKeys() throws Exception {
|
||||||
long rotationPeriod = 123, rotationPeriod1 = 234;
|
long timePeriod = 123, timePeriod1 = 234;
|
||||||
boolean active = random.nextBoolean();
|
boolean active = random.nextBoolean();
|
||||||
TransportKeys keys = createTransportKeys(rotationPeriod, active);
|
TransportKeys keys = createTransportKeys(timePeriod, active);
|
||||||
TransportKeys keys1 = createTransportKeys(rotationPeriod1, active);
|
TransportKeys keys1 = createTransportKeys(timePeriod1, active);
|
||||||
|
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
@@ -690,43 +650,44 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
|
assertEquals(emptyList(), db.getTransportKeys(txn, transportId));
|
||||||
|
|
||||||
// Add the contact, the transport and the transport keys
|
// Add the contact, the transport and the transport keys
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, active));
|
|
||||||
db.addTransport(txn, transportId, 123);
|
db.addTransport(txn, transportId, 123);
|
||||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||||
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
|
assertEquals(keySetId1, db.addTransportKeys(txn, contactId, keys1));
|
||||||
|
|
||||||
// Retrieve the transport keys
|
// Retrieve the transport keys
|
||||||
Collection<KeySet> allKeys = db.getTransportKeys(txn, transportId);
|
Collection<TransportKeySet> allKeys =
|
||||||
|
db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(2, allKeys.size());
|
assertEquals(2, allKeys.size());
|
||||||
for (KeySet ks : allKeys) {
|
for (TransportKeySet ks : allKeys) {
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
if (ks.getKeySetId().equals(keySetId)) {
|
if (ks.getKeySetId().equals(keySetId)) {
|
||||||
assertKeysEquals(keys, ks.getTransportKeys());
|
assertKeysEquals(keys, ks.getKeys());
|
||||||
} else {
|
} else {
|
||||||
assertEquals(keySetId1, ks.getKeySetId());
|
assertEquals(keySetId1, ks.getKeySetId());
|
||||||
assertKeysEquals(keys1, ks.getTransportKeys());
|
assertKeysEquals(keys1, ks.getKeys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rotate the transport keys
|
// Rotate the transport keys
|
||||||
TransportKeys rotated = createTransportKeys(rotationPeriod + 1, active);
|
TransportKeys rotated = createTransportKeys(timePeriod + 1, active);
|
||||||
TransportKeys rotated1 =
|
TransportKeys rotated1 =
|
||||||
createTransportKeys(rotationPeriod1 + 1, active);
|
createTransportKeys(timePeriod1 + 1, active);
|
||||||
db.updateTransportKeys(txn, new KeySet(keySetId, contactId, rotated));
|
db.updateTransportKeys(txn, new TransportKeySet(keySetId, contactId,
|
||||||
db.updateTransportKeys(txn, new KeySet(keySetId1, contactId, rotated1));
|
rotated));
|
||||||
|
db.updateTransportKeys(txn, new TransportKeySet(keySetId1, contactId,
|
||||||
|
rotated1));
|
||||||
|
|
||||||
// Retrieve the transport keys again
|
// Retrieve the transport keys again
|
||||||
allKeys = db.getTransportKeys(txn, transportId);
|
allKeys = db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(2, allKeys.size());
|
assertEquals(2, allKeys.size());
|
||||||
for (KeySet ks : allKeys) {
|
for (TransportKeySet ks : allKeys) {
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
if (ks.getKeySetId().equals(keySetId)) {
|
if (ks.getKeySetId().equals(keySetId)) {
|
||||||
assertKeysEquals(rotated, ks.getTransportKeys());
|
assertKeysEquals(rotated, ks.getKeys());
|
||||||
} else {
|
} else {
|
||||||
assertEquals(keySetId1, ks.getKeySetId());
|
assertEquals(keySetId1, ks.getKeySetId());
|
||||||
assertKeysEquals(rotated1, ks.getTransportKeys());
|
assertKeysEquals(rotated1, ks.getKeys());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -741,7 +702,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
private void assertKeysEquals(TransportKeys expected,
|
private void assertKeysEquals(TransportKeys expected,
|
||||||
TransportKeys actual) {
|
TransportKeys actual) {
|
||||||
assertEquals(expected.getTransportId(), actual.getTransportId());
|
assertEquals(expected.getTransportId(), actual.getTransportId());
|
||||||
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod());
|
assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
|
||||||
assertKeysEquals(expected.getPreviousIncomingKeys(),
|
assertKeysEquals(expected.getPreviousIncomingKeys(),
|
||||||
actual.getPreviousIncomingKeys());
|
actual.getPreviousIncomingKeys());
|
||||||
assertKeysEquals(expected.getCurrentIncomingKeys(),
|
assertKeysEquals(expected.getCurrentIncomingKeys(),
|
||||||
@@ -757,7 +718,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
actual.getTagKey().getBytes());
|
actual.getTagKey().getBytes());
|
||||||
assertArrayEquals(expected.getHeaderKey().getBytes(),
|
assertArrayEquals(expected.getHeaderKey().getBytes(),
|
||||||
actual.getHeaderKey().getBytes());
|
actual.getHeaderKey().getBytes());
|
||||||
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod());
|
assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
|
||||||
assertEquals(expected.getWindowBase(), actual.getWindowBase());
|
assertEquals(expected.getWindowBase(), actual.getWindowBase());
|
||||||
assertArrayEquals(expected.getWindowBitmap(), actual.getWindowBitmap());
|
assertArrayEquals(expected.getWindowBitmap(), actual.getWindowBitmap());
|
||||||
}
|
}
|
||||||
@@ -767,39 +728,240 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
actual.getTagKey().getBytes());
|
actual.getTagKey().getBytes());
|
||||||
assertArrayEquals(expected.getHeaderKey().getBytes(),
|
assertArrayEquals(expected.getHeaderKey().getBytes(),
|
||||||
actual.getHeaderKey().getBytes());
|
actual.getHeaderKey().getBytes());
|
||||||
assertEquals(expected.getRotationPeriod(), actual.getRotationPeriod());
|
assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
|
||||||
assertEquals(expected.getStreamCounter(), actual.getStreamCounter());
|
assertEquals(expected.getStreamCounter(), actual.getStreamCounter());
|
||||||
assertEquals(expected.isActive(), actual.isActive());
|
assertEquals(expected.isActive(), actual.isActive());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeys() throws Exception {
|
||||||
|
long timePeriod = 123, timePeriod1 = 234;
|
||||||
|
boolean alice = random.nextBoolean();
|
||||||
|
SecretKey rootKey = getSecretKey();
|
||||||
|
SecretKey rootKey1 = getSecretKey();
|
||||||
|
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
||||||
|
HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
|
||||||
|
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// Initially there should be no handshake keys in the database
|
||||||
|
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
|
||||||
|
|
||||||
|
// Add the contact, the transport and the handshake keys
|
||||||
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
|
db.addTransport(txn, transportId, 123);
|
||||||
|
assertEquals(handshakeKeySetId,
|
||||||
|
db.addHandshakeKeys(txn, contactId, keys));
|
||||||
|
assertEquals(handshakeKeySetId1,
|
||||||
|
db.addHandshakeKeys(txn, contactId, keys1));
|
||||||
|
|
||||||
|
// Retrieve the handshake keys
|
||||||
|
Collection<HandshakeKeySet> allKeys =
|
||||||
|
db.getHandshakeKeys(txn, transportId);
|
||||||
|
assertEquals(2, allKeys.size());
|
||||||
|
for (HandshakeKeySet ks : allKeys) {
|
||||||
|
assertEquals(contactId, ks.getContactId());
|
||||||
|
assertNull(ks.getPendingContactId());
|
||||||
|
if (ks.getKeySetId().equals(handshakeKeySetId)) {
|
||||||
|
assertKeysEquals(keys, ks.getKeys());
|
||||||
|
} else {
|
||||||
|
assertEquals(handshakeKeySetId1, ks.getKeySetId());
|
||||||
|
assertKeysEquals(keys1, ks.getKeys());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the handshake keys
|
||||||
|
HandshakeKeys updated =
|
||||||
|
createHandshakeKeys(timePeriod + 1, rootKey, alice);
|
||||||
|
HandshakeKeys updated1 =
|
||||||
|
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
|
||||||
|
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
|
||||||
|
contactId, updated));
|
||||||
|
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
|
||||||
|
contactId, updated1));
|
||||||
|
|
||||||
|
// Retrieve the handshake keys again
|
||||||
|
allKeys = db.getHandshakeKeys(txn, transportId);
|
||||||
|
assertEquals(2, allKeys.size());
|
||||||
|
for (HandshakeKeySet ks : allKeys) {
|
||||||
|
assertEquals(contactId, ks.getContactId());
|
||||||
|
assertNull(ks.getPendingContactId());
|
||||||
|
if (ks.getKeySetId().equals(handshakeKeySetId)) {
|
||||||
|
assertKeysEquals(updated, ks.getKeys());
|
||||||
|
} else {
|
||||||
|
assertEquals(handshakeKeySetId1, ks.getKeySetId());
|
||||||
|
assertKeysEquals(updated1, ks.getKeys());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removing the contact should remove the handshake keys
|
||||||
|
db.removeContact(txn, contactId);
|
||||||
|
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
|
||||||
|
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void assertKeysEquals(HandshakeKeys expected,
|
||||||
|
HandshakeKeys actual) {
|
||||||
|
assertEquals(expected.getTransportId(), actual.getTransportId());
|
||||||
|
assertEquals(expected.getTimePeriod(), actual.getTimePeriod());
|
||||||
|
assertArrayEquals(expected.getRootKey().getBytes(),
|
||||||
|
actual.getRootKey().getBytes());
|
||||||
|
assertEquals(expected.isAlice(), actual.isAlice());
|
||||||
|
assertKeysEquals(expected.getPreviousIncomingKeys(),
|
||||||
|
actual.getPreviousIncomingKeys());
|
||||||
|
assertKeysEquals(expected.getCurrentIncomingKeys(),
|
||||||
|
actual.getCurrentIncomingKeys());
|
||||||
|
assertKeysEquals(expected.getNextIncomingKeys(),
|
||||||
|
actual.getNextIncomingKeys());
|
||||||
|
assertKeysEquals(expected.getCurrentOutgoingKeys(),
|
||||||
|
actual.getCurrentOutgoingKeys());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testHandshakeKeysForPendingContact() throws Exception {
|
||||||
|
long timePeriod = 123, timePeriod1 = 234;
|
||||||
|
boolean alice = random.nextBoolean();
|
||||||
|
SecretKey rootKey = getSecretKey();
|
||||||
|
SecretKey rootKey1 = getSecretKey();
|
||||||
|
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
||||||
|
HandshakeKeys keys1 = createHandshakeKeys(timePeriod1, rootKey1, alice);
|
||||||
|
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// Initially there should be no handshake keys in the database
|
||||||
|
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
|
||||||
|
|
||||||
|
// Add the pending contact, the transport and the handshake keys
|
||||||
|
db.addPendingContact(txn, pendingContact);
|
||||||
|
db.addTransport(txn, transportId, 123);
|
||||||
|
assertEquals(handshakeKeySetId, db.addHandshakeKeys(txn,
|
||||||
|
pendingContact.getId(), keys));
|
||||||
|
assertEquals(handshakeKeySetId1, db.addHandshakeKeys(txn,
|
||||||
|
pendingContact.getId(), keys1));
|
||||||
|
|
||||||
|
// Retrieve the handshake keys
|
||||||
|
Collection<HandshakeKeySet> allKeys =
|
||||||
|
db.getHandshakeKeys(txn, transportId);
|
||||||
|
assertEquals(2, allKeys.size());
|
||||||
|
for (HandshakeKeySet ks : allKeys) {
|
||||||
|
assertNull(ks.getContactId());
|
||||||
|
assertEquals(pendingContact.getId(), ks.getPendingContactId());
|
||||||
|
if (ks.getKeySetId().equals(handshakeKeySetId)) {
|
||||||
|
assertKeysEquals(keys, ks.getKeys());
|
||||||
|
} else {
|
||||||
|
assertEquals(handshakeKeySetId1, ks.getKeySetId());
|
||||||
|
assertKeysEquals(keys1, ks.getKeys());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update the handshake keys
|
||||||
|
HandshakeKeys updated =
|
||||||
|
createHandshakeKeys(timePeriod + 1, rootKey, alice);
|
||||||
|
HandshakeKeys updated1 =
|
||||||
|
createHandshakeKeys(timePeriod1 + 1, rootKey1, alice);
|
||||||
|
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId,
|
||||||
|
pendingContact.getId(), updated));
|
||||||
|
db.updateHandshakeKeys(txn, new HandshakeKeySet(handshakeKeySetId1,
|
||||||
|
pendingContact.getId(), updated1));
|
||||||
|
|
||||||
|
// Retrieve the handshake keys again
|
||||||
|
allKeys = db.getHandshakeKeys(txn, transportId);
|
||||||
|
assertEquals(2, allKeys.size());
|
||||||
|
for (HandshakeKeySet ks : allKeys) {
|
||||||
|
assertNull(ks.getContactId());
|
||||||
|
assertEquals(pendingContact.getId(), ks.getPendingContactId());
|
||||||
|
if (ks.getKeySetId().equals(handshakeKeySetId)) {
|
||||||
|
assertKeysEquals(updated, ks.getKeys());
|
||||||
|
} else {
|
||||||
|
assertEquals(handshakeKeySetId1, ks.getKeySetId());
|
||||||
|
assertKeysEquals(updated1, ks.getKeys());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removing the pending contact should remove the handshake keys
|
||||||
|
db.removePendingContact(txn, pendingContact.getId());
|
||||||
|
assertEquals(emptyList(), db.getHandshakeKeys(txn, transportId));
|
||||||
|
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIncrementStreamCounter() throws Exception {
|
public void testIncrementStreamCounter() throws Exception {
|
||||||
long rotationPeriod = 123;
|
long timePeriod = 123;
|
||||||
TransportKeys keys = createTransportKeys(rotationPeriod, true);
|
TransportKeys keys = createTransportKeys(timePeriod, true);
|
||||||
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
|
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
|
||||||
|
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add the contact, transport and transport keys
|
// Add the contact, transport and transport keys
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addTransport(txn, transportId, 123);
|
db.addTransport(txn, transportId, 123);
|
||||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||||
|
|
||||||
// Increment the stream counter twice and retrieve the transport keys
|
// Increment the stream counter twice and retrieve the transport keys
|
||||||
db.incrementStreamCounter(txn, transportId, keySetId);
|
db.incrementStreamCounter(txn, transportId, keySetId);
|
||||||
db.incrementStreamCounter(txn, transportId, keySetId);
|
db.incrementStreamCounter(txn, transportId, keySetId);
|
||||||
Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId);
|
Collection<TransportKeySet> newKeys =
|
||||||
|
db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(1, newKeys.size());
|
assertEquals(1, newKeys.size());
|
||||||
KeySet ks = newKeys.iterator().next();
|
TransportKeySet ks = newKeys.iterator().next();
|
||||||
assertEquals(keySetId, ks.getKeySetId());
|
assertEquals(keySetId, ks.getKeySetId());
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
TransportKeys k = ks.getTransportKeys();
|
TransportKeys k = ks.getKeys();
|
||||||
assertEquals(transportId, k.getTransportId());
|
assertEquals(transportId, k.getTransportId());
|
||||||
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||||
assertEquals(rotationPeriod, outCurr.getRotationPeriod());
|
assertEquals(timePeriod, outCurr.getTimePeriod());
|
||||||
|
assertEquals(streamCounter + 2, outCurr.getStreamCounter());
|
||||||
|
|
||||||
|
// The rest of the keys should be unaffected
|
||||||
|
assertKeysEquals(keys.getPreviousIncomingKeys(),
|
||||||
|
k.getPreviousIncomingKeys());
|
||||||
|
assertKeysEquals(keys.getCurrentIncomingKeys(),
|
||||||
|
k.getCurrentIncomingKeys());
|
||||||
|
assertKeysEquals(keys.getNextIncomingKeys(), k.getNextIncomingKeys());
|
||||||
|
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testIncrementStreamCounterForHandshakeKeys() throws Exception {
|
||||||
|
long timePeriod = 123;
|
||||||
|
SecretKey rootKey = getSecretKey();
|
||||||
|
boolean alice = random.nextBoolean();
|
||||||
|
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
||||||
|
long streamCounter = keys.getCurrentOutgoingKeys().getStreamCounter();
|
||||||
|
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// Add the contact, transport and handshake keys
|
||||||
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
|
db.addTransport(txn, transportId, 123);
|
||||||
|
assertEquals(handshakeKeySetId,
|
||||||
|
db.addHandshakeKeys(txn, contactId, keys));
|
||||||
|
|
||||||
|
// Increment the stream counter twice and retrieve the handshake keys
|
||||||
|
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
|
||||||
|
db.incrementStreamCounter(txn, transportId, handshakeKeySetId);
|
||||||
|
Collection<HandshakeKeySet> newKeys =
|
||||||
|
db.getHandshakeKeys(txn, transportId);
|
||||||
|
assertEquals(1, newKeys.size());
|
||||||
|
HandshakeKeySet ks = newKeys.iterator().next();
|
||||||
|
assertEquals(handshakeKeySetId, ks.getKeySetId());
|
||||||
|
assertEquals(contactId, ks.getContactId());
|
||||||
|
HandshakeKeys k = ks.getKeys();
|
||||||
|
assertEquals(transportId, k.getTransportId());
|
||||||
|
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
|
||||||
|
assertEquals(alice, k.isAlice());
|
||||||
|
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
|
||||||
|
assertEquals(timePeriod, outCurr.getTimePeriod());
|
||||||
assertEquals(streamCounter + 2, outCurr.getStreamCounter());
|
assertEquals(streamCounter + 2, outCurr.getStreamCounter());
|
||||||
|
|
||||||
// The rest of the keys should be unaffected
|
// The rest of the keys should be unaffected
|
||||||
@@ -816,8 +978,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testSetReorderingWindow() throws Exception {
|
public void testSetReorderingWindow() throws Exception {
|
||||||
boolean active = random.nextBoolean();
|
boolean active = random.nextBoolean();
|
||||||
long rotationPeriod = 123;
|
long timePeriod = 123;
|
||||||
TransportKeys keys = createTransportKeys(rotationPeriod, active);
|
TransportKeys keys = createTransportKeys(timePeriod, active);
|
||||||
long base = keys.getCurrentIncomingKeys().getWindowBase();
|
long base = keys.getCurrentIncomingKeys().getWindowBase();
|
||||||
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
|
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
|
||||||
|
|
||||||
@@ -825,25 +987,24 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add the contact, transport and transport keys
|
// Add the contact, transport and transport keys
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, active));
|
|
||||||
db.addTransport(txn, transportId, 123);
|
db.addTransport(txn, transportId, 123);
|
||||||
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
assertEquals(keySetId, db.addTransportKeys(txn, contactId, keys));
|
||||||
|
|
||||||
// Update the reordering window and retrieve the transport keys
|
// Update the reordering window and retrieve the transport keys
|
||||||
random.nextBytes(bitmap);
|
random.nextBytes(bitmap);
|
||||||
db.setReorderingWindow(txn, keySetId, transportId, rotationPeriod,
|
db.setReorderingWindow(txn, keySetId, transportId, timePeriod,
|
||||||
base + 1, bitmap);
|
base + 1, bitmap);
|
||||||
Collection<KeySet> newKeys = db.getTransportKeys(txn, transportId);
|
Collection<TransportKeySet> newKeys =
|
||||||
|
db.getTransportKeys(txn, transportId);
|
||||||
assertEquals(1, newKeys.size());
|
assertEquals(1, newKeys.size());
|
||||||
KeySet ks = newKeys.iterator().next();
|
TransportKeySet ks = newKeys.iterator().next();
|
||||||
assertEquals(keySetId, ks.getKeySetId());
|
assertEquals(keySetId, ks.getKeySetId());
|
||||||
assertEquals(contactId, ks.getContactId());
|
assertEquals(contactId, ks.getContactId());
|
||||||
TransportKeys k = ks.getTransportKeys();
|
TransportKeys k = ks.getKeys();
|
||||||
assertEquals(transportId, k.getTransportId());
|
assertEquals(transportId, k.getTransportId());
|
||||||
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||||
assertEquals(rotationPeriod, inCurr.getRotationPeriod());
|
assertEquals(timePeriod, inCurr.getTimePeriod());
|
||||||
assertEquals(base + 1, inCurr.getWindowBase());
|
assertEquals(base + 1, inCurr.getWindowBase());
|
||||||
assertArrayEquals(bitmap, inCurr.getWindowBitmap());
|
assertArrayEquals(bitmap, inCurr.getWindowBitmap());
|
||||||
|
|
||||||
@@ -859,54 +1020,90 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetContactsByAuthorId() throws Exception {
|
public void testSetReorderingWindowForHandshakeKeys() throws Exception {
|
||||||
|
long timePeriod = 123;
|
||||||
|
SecretKey rootKey = getSecretKey();
|
||||||
|
boolean alice = random.nextBoolean();
|
||||||
|
HandshakeKeys keys = createHandshakeKeys(timePeriod, rootKey, alice);
|
||||||
|
long base = keys.getCurrentIncomingKeys().getWindowBase();
|
||||||
|
byte[] bitmap = keys.getCurrentIncomingKeys().getWindowBitmap();
|
||||||
|
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a local author - no contacts should be associated
|
// Add the contact, transport and handshake keys
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author,true));
|
||||||
|
db.addTransport(txn, transportId, 123);
|
||||||
|
assertEquals(handshakeKeySetId,
|
||||||
|
db.addHandshakeKeys(txn, contactId, keys));
|
||||||
|
|
||||||
// Add a contact associated with the local author
|
// Update the reordering window and retrieve the handshake keys
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
random.nextBytes(bitmap);
|
||||||
true, true));
|
db.setReorderingWindow(txn, handshakeKeySetId, transportId, timePeriod,
|
||||||
|
base + 1, bitmap);
|
||||||
|
Collection<HandshakeKeySet> newKeys =
|
||||||
|
db.getHandshakeKeys(txn, transportId);
|
||||||
|
assertEquals(1, newKeys.size());
|
||||||
|
HandshakeKeySet ks = newKeys.iterator().next();
|
||||||
|
assertEquals(handshakeKeySetId, ks.getKeySetId());
|
||||||
|
assertEquals(contactId, ks.getContactId());
|
||||||
|
HandshakeKeys k = ks.getKeys();
|
||||||
|
assertEquals(transportId, k.getTransportId());
|
||||||
|
assertArrayEquals(rootKey.getBytes(), k.getRootKey().getBytes());
|
||||||
|
assertEquals(alice, k.isAlice());
|
||||||
|
IncomingKeys inCurr = k.getCurrentIncomingKeys();
|
||||||
|
assertEquals(timePeriod, inCurr.getTimePeriod());
|
||||||
|
assertEquals(base + 1, inCurr.getWindowBase());
|
||||||
|
assertArrayEquals(bitmap, inCurr.getWindowBitmap());
|
||||||
|
|
||||||
// Ensure contact is returned from database by Author ID
|
// The rest of the keys should be unaffected
|
||||||
Collection<Contact> contacts =
|
assertKeysEquals(keys.getPreviousIncomingKeys(),
|
||||||
db.getContactsByAuthorId(txn, author.getId());
|
k.getPreviousIncomingKeys());
|
||||||
assertEquals(1, contacts.size());
|
assertKeysEquals(keys.getNextIncomingKeys(), k.getNextIncomingKeys());
|
||||||
assertEquals(contactId, contacts.iterator().next().getId());
|
assertKeysEquals(keys.getCurrentOutgoingKeys(),
|
||||||
|
k.getCurrentOutgoingKeys());
|
||||||
// Ensure no contacts are returned after contact was deleted
|
|
||||||
db.removeContact(txn, contactId);
|
|
||||||
contacts = db.getContactsByAuthorId(txn, author.getId());
|
|
||||||
assertEquals(0, contacts.size());
|
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testGetContactsByLocalAuthorId() throws Exception {
|
public void testGetContactByContactId() throws Exception {
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a local author - no contacts should be associated
|
// Add a contact
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
Collection<ContactId> contacts =
|
|
||||||
db.getContacts(txn, localAuthor.getId());
|
|
||||||
assertEquals(emptyList(), contacts);
|
|
||||||
|
|
||||||
// Add a contact associated with the local author
|
// Check the contact is returned
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
Contact c = db.getContact(txn, contactId);
|
||||||
true, true));
|
assertEquals(contactId, c.getId());
|
||||||
contacts = db.getContacts(txn, localAuthor.getId());
|
assertEquals(author.getId(), c.getAuthor().getId());
|
||||||
assertEquals(singletonList(contactId), contacts);
|
assertEquals(author.getFormatVersion(),
|
||||||
|
c.getAuthor().getFormatVersion());
|
||||||
|
assertEquals(author.getName(), c.getAuthor().getName());
|
||||||
|
assertArrayEquals(author.getPublicKey(), c.getAuthor().getPublicKey());
|
||||||
|
|
||||||
// Remove the local author - the contact should be removed
|
db.commitTransaction(txn);
|
||||||
db.removeLocalAuthor(txn, localAuthor.getId());
|
db.close();
|
||||||
contacts = db.getContacts(txn, localAuthor.getId());
|
}
|
||||||
assertEquals(emptyList(), contacts);
|
|
||||||
assertFalse(db.containsContact(txn, contactId));
|
@Test
|
||||||
|
public void testGetContactByAuthorId() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
// Add a contact
|
||||||
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
|
|
||||||
|
// Check the contact is returned
|
||||||
|
Contact c = db.getContact(txn, author.getId());
|
||||||
|
assertEquals(contactId, c.getId());
|
||||||
|
assertEquals(author.getId(), c.getAuthor().getId());
|
||||||
|
assertEquals(author.getFormatVersion(),
|
||||||
|
c.getAuthor().getFormatVersion());
|
||||||
|
assertEquals(author.getName(), c.getAuthor().getName());
|
||||||
|
assertArrayEquals(author.getPublicKey(), c.getAuthor().getPublicKey());
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
db.commitTransaction(txn);
|
||||||
db.close();
|
db.close();
|
||||||
@@ -918,9 +1115,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact - initially there should be no offered messages
|
// Add a contact - initially there should be no offered messages
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
assertEquals(0, db.countOfferedMessages(txn, contactId));
|
assertEquals(0, db.countOfferedMessages(txn, contactId));
|
||||||
|
|
||||||
// Add some offered messages and count them
|
// Add some offered messages and count them
|
||||||
@@ -1502,9 +1697,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
@@ -1601,43 +1794,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDifferentLocalAuthorsCanHaveTheSameContact()
|
|
||||||
throws Exception {
|
|
||||||
LocalAuthor localAuthor1 = getLocalAuthor();
|
|
||||||
|
|
||||||
Database<Connection> db = open(false);
|
|
||||||
Connection txn = db.startTransaction();
|
|
||||||
|
|
||||||
// Add two local authors
|
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
|
||||||
db.addLocalAuthor(txn, localAuthor1);
|
|
||||||
|
|
||||||
// Add the same contact for each local author
|
|
||||||
ContactId contactId =
|
|
||||||
db.addContact(txn, author, localAuthor.getId(), true, true);
|
|
||||||
ContactId contactId1 =
|
|
||||||
db.addContact(txn, author, localAuthor1.getId(), true, true);
|
|
||||||
|
|
||||||
// The contacts should be distinct
|
|
||||||
assertNotEquals(contactId, contactId1);
|
|
||||||
assertEquals(2, db.getContacts(txn).size());
|
|
||||||
assertEquals(1, db.getContacts(txn, localAuthor.getId()).size());
|
|
||||||
assertEquals(1, db.getContacts(txn, localAuthor1.getId()).size());
|
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testDeleteMessage() throws Exception {
|
public void testDeleteMessage() throws Exception {
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
@@ -1683,47 +1846,13 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testSetContactActive() throws Exception {
|
|
||||||
Database<Connection> db = open(false);
|
|
||||||
Connection txn = db.startTransaction();
|
|
||||||
|
|
||||||
// Add a contact
|
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
|
|
||||||
// The contact should be active
|
|
||||||
Contact contact = db.getContact(txn, contactId);
|
|
||||||
assertTrue(contact.isActive());
|
|
||||||
|
|
||||||
// Set the contact inactive
|
|
||||||
db.setContactActive(txn, contactId, false);
|
|
||||||
|
|
||||||
// The contact should be inactive
|
|
||||||
contact = db.getContact(txn, contactId);
|
|
||||||
assertFalse(contact.isActive());
|
|
||||||
|
|
||||||
// Set the contact active
|
|
||||||
db.setContactActive(txn, contactId, true);
|
|
||||||
|
|
||||||
// The contact should be active
|
|
||||||
contact = db.getContact(txn, contactId);
|
|
||||||
assertTrue(contact.isActive());
|
|
||||||
|
|
||||||
db.commitTransaction(txn);
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testSetContactAlias() throws Exception {
|
public void testSetContactAlias() throws Exception {
|
||||||
Database<Connection> db = open(false);
|
Database<Connection> db = open(false);
|
||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact
|
// Add a contact
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
|
|
||||||
// The contact should have no alias
|
// The contact should have no alias
|
||||||
Contact contact = db.getContact(txn, contactId);
|
Contact contact = db.getContact(txn, contactId);
|
||||||
@@ -1778,9 +1907,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a group and a message
|
// Add a contact, a group and a message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addMessage(txn, message, UNKNOWN, false, null);
|
db.addMessage(txn, message, UNKNOWN, false, null);
|
||||||
|
|
||||||
@@ -1862,9 +1989,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
@@ -1907,9 +2032,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
Connection txn = db.startTransaction();
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
// Add a contact, a shared group and a shared message
|
// Add a contact, a shared group and a shared message
|
||||||
db.addLocalAuthor(txn, localAuthor);
|
assertEquals(contactId, db.addContact(txn, author, true));
|
||||||
assertEquals(contactId, db.addContact(txn, author, localAuthor.getId(),
|
|
||||||
true, true));
|
|
||||||
db.addGroup(txn, group);
|
db.addGroup(txn, group);
|
||||||
db.addGroupVisibility(txn, contactId, groupId, true);
|
db.addGroupVisibility(txn, contactId, groupId, true);
|
||||||
db.addMessage(txn, message, DELIVERED, true, null);
|
db.addMessage(txn, message, DELIVERED, true, null);
|
||||||
@@ -1990,41 +2113,94 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
|
|||||||
db.close();
|
db.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPendingContacts() throws Exception {
|
||||||
|
Database<Connection> db = open(false);
|
||||||
|
Connection txn = db.startTransaction();
|
||||||
|
|
||||||
|
assertEquals(emptyList(), db.getPendingContacts(txn));
|
||||||
|
|
||||||
|
db.addPendingContact(txn, pendingContact);
|
||||||
|
Collection<PendingContact> pendingContacts =
|
||||||
|
db.getPendingContacts(txn);
|
||||||
|
assertEquals(1, pendingContacts.size());
|
||||||
|
PendingContact retrieved = pendingContacts.iterator().next();
|
||||||
|
assertEquals(pendingContact.getId(), retrieved.getId());
|
||||||
|
assertEquals(pendingContact.getAlias(), retrieved.getAlias());
|
||||||
|
assertEquals(pendingContact.getState(), retrieved.getState());
|
||||||
|
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
|
||||||
|
|
||||||
|
db.setPendingContactState(txn, pendingContact.getId(), FAILED);
|
||||||
|
pendingContacts = db.getPendingContacts(txn);
|
||||||
|
assertEquals(1, pendingContacts.size());
|
||||||
|
retrieved = pendingContacts.iterator().next();
|
||||||
|
assertEquals(pendingContact.getId(), retrieved.getId());
|
||||||
|
assertEquals(pendingContact.getAlias(), retrieved.getAlias());
|
||||||
|
assertEquals(FAILED, retrieved.getState());
|
||||||
|
assertEquals(pendingContact.getTimestamp(), retrieved.getTimestamp());
|
||||||
|
|
||||||
|
db.removePendingContact(txn, pendingContact.getId());
|
||||||
|
assertEquals(emptyList(), db.getPendingContacts(txn));
|
||||||
|
|
||||||
|
db.commitTransaction(txn);
|
||||||
|
db.close();
|
||||||
|
}
|
||||||
|
|
||||||
private Database<Connection> open(boolean resume) throws Exception {
|
private Database<Connection> open(boolean resume) throws Exception {
|
||||||
return open(resume, new TestMessageFactory(), new SystemClock());
|
return open(resume, new TestMessageFactory(), new SystemClock());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Database<Connection> open(boolean resume,
|
private Database<Connection> open(boolean resume,
|
||||||
MessageFactory messageFactory, Clock clock) throws Exception {
|
MessageFactory messageFactory, Clock clock) throws Exception {
|
||||||
Database<Connection> db =
|
Database<Connection> db = createDatabase(
|
||||||
createDatabase(new TestDatabaseConfig(testDir, MAX_SIZE),
|
new TestDatabaseConfig(testDir), messageFactory, clock);
|
||||||
messageFactory, clock);
|
|
||||||
if (!resume) deleteTestDirectory(testDir);
|
if (!resume) deleteTestDirectory(testDir);
|
||||||
db.open(key, null);
|
db.open(key, null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TransportKeys createTransportKeys(long rotationPeriod,
|
private TransportKeys createTransportKeys(long timePeriod, boolean active) {
|
||||||
boolean active) {
|
|
||||||
SecretKey inPrevTagKey = getSecretKey();
|
SecretKey inPrevTagKey = getSecretKey();
|
||||||
SecretKey inPrevHeaderKey = getSecretKey();
|
SecretKey inPrevHeaderKey = getSecretKey();
|
||||||
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
|
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
|
||||||
rotationPeriod - 1, 123, new byte[4]);
|
timePeriod - 1, 123, new byte[4]);
|
||||||
SecretKey inCurrTagKey = getSecretKey();
|
SecretKey inCurrTagKey = getSecretKey();
|
||||||
SecretKey inCurrHeaderKey = getSecretKey();
|
SecretKey inCurrHeaderKey = getSecretKey();
|
||||||
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
|
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
|
||||||
rotationPeriod, 234, new byte[4]);
|
timePeriod, 234, new byte[4]);
|
||||||
SecretKey inNextTagKey = getSecretKey();
|
SecretKey inNextTagKey = getSecretKey();
|
||||||
SecretKey inNextHeaderKey = getSecretKey();
|
SecretKey inNextHeaderKey = getSecretKey();
|
||||||
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
|
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
|
||||||
rotationPeriod + 1, 345, new byte[4]);
|
timePeriod + 1, 345, new byte[4]);
|
||||||
SecretKey outCurrTagKey = getSecretKey();
|
SecretKey outCurrTagKey = getSecretKey();
|
||||||
SecretKey outCurrHeaderKey = getSecretKey();
|
SecretKey outCurrHeaderKey = getSecretKey();
|
||||||
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
|
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
|
||||||
rotationPeriod, 456, active);
|
timePeriod, 456, active);
|
||||||
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
return new TransportKeys(transportId, inPrev, inCurr, inNext, outCurr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private HandshakeKeys createHandshakeKeys(long timePeriod,
|
||||||
|
SecretKey rootKey, boolean alice) {
|
||||||
|
SecretKey inPrevTagKey = getSecretKey();
|
||||||
|
SecretKey inPrevHeaderKey = getSecretKey();
|
||||||
|
IncomingKeys inPrev = new IncomingKeys(inPrevTagKey, inPrevHeaderKey,
|
||||||
|
timePeriod - 1, 123, new byte[4]);
|
||||||
|
SecretKey inCurrTagKey = getSecretKey();
|
||||||
|
SecretKey inCurrHeaderKey = getSecretKey();
|
||||||
|
IncomingKeys inCurr = new IncomingKeys(inCurrTagKey, inCurrHeaderKey,
|
||||||
|
timePeriod, 234, new byte[4]);
|
||||||
|
SecretKey inNextTagKey = getSecretKey();
|
||||||
|
SecretKey inNextHeaderKey = getSecretKey();
|
||||||
|
IncomingKeys inNext = new IncomingKeys(inNextTagKey, inNextHeaderKey,
|
||||||
|
timePeriod + 1, 345, new byte[4]);
|
||||||
|
SecretKey outCurrTagKey = getSecretKey();
|
||||||
|
SecretKey outCurrHeaderKey = getSecretKey();
|
||||||
|
OutgoingKeys outCurr = new OutgoingKeys(outCurrTagKey, outCurrHeaderKey,
|
||||||
|
timePeriod, 456, true);
|
||||||
|
return new HandshakeKeys(transportId, inPrev, inCurr, inNext, outCurr,
|
||||||
|
rootKey, alice);
|
||||||
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
deleteTestDirectory(testDir);
|
deleteTestDirectory(testDir);
|
||||||
|
|||||||
@@ -45,8 +45,8 @@ public abstract class SingleDatabasePerformanceTest
|
|||||||
|
|
||||||
private Database<Connection> openDatabase() throws DbException {
|
private Database<Connection> openDatabase() throws DbException {
|
||||||
Database<Connection> db = createDatabase(
|
Database<Connection> db = createDatabase(
|
||||||
new TestDatabaseConfig(testDir, MAX_SIZE),
|
new TestDatabaseConfig(testDir), new TestMessageFactory(),
|
||||||
new TestMessageFactory(), new SystemClock());
|
new SystemClock());
|
||||||
db.open(databaseKey, null);
|
db.open(databaseKey, null);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import org.junit.Rule;
|
|||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_SECRET_LABEL;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.MASTER_KEY_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;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||||
@@ -74,7 +74,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
Payload ourPayload = new Payload(aliceCommit, null);
|
Payload ourPayload = new Payload(aliceCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
SecretKey masterSecret = getSecretKey();
|
SecretKey masterKey = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
@@ -134,13 +134,13 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
true, false);
|
true, false);
|
||||||
will(returnValue(bobConfirm));
|
will(returnValue(bobConfirm));
|
||||||
|
|
||||||
// Alice computes master secret
|
// Alice derives master key
|
||||||
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
oneOf(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
|
||||||
will(returnValue(masterSecret));
|
will(returnValue(masterKey));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
assertThat(masterSecret, is(equalTo(protocol.perform())));
|
assertThat(masterKey, is(equalTo(protocol.perform())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -150,7 +150,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
Payload ourPayload = new Payload(bobCommit, null);
|
Payload ourPayload = new Payload(bobCommit, null);
|
||||||
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
KeyPair ourKeyPair = new KeyPair(ourPubKey, null);
|
||||||
SecretKey sharedSecret = getSecretKey();
|
SecretKey sharedSecret = getSecretKey();
|
||||||
SecretKey masterSecret = getSecretKey();
|
SecretKey masterKey = getSecretKey();
|
||||||
|
|
||||||
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
KeyAgreementProtocol protocol = new KeyAgreementProtocol(callbacks,
|
||||||
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
crypto, keyAgreementCrypto, payloadEncoder, transport,
|
||||||
@@ -209,13 +209,13 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
will(returnValue(bobConfirm));
|
will(returnValue(bobConfirm));
|
||||||
oneOf(transport).sendConfirm(bobConfirm);
|
oneOf(transport).sendConfirm(bobConfirm);
|
||||||
|
|
||||||
// Bob computes master secret
|
// Bob derives master key
|
||||||
oneOf(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
oneOf(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
|
||||||
will(returnValue(masterSecret));
|
will(returnValue(masterKey));
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
assertThat(masterSecret, is(equalTo(protocol.perform())));
|
assertThat(masterKey, is(equalTo(protocol.perform())));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = AbortException.class)
|
@Test(expected = AbortException.class)
|
||||||
@@ -373,8 +373,8 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
|
|||||||
// Alice aborts
|
// Alice aborts
|
||||||
oneOf(transport).sendAbort(false);
|
oneOf(transport).sendAbort(false);
|
||||||
|
|
||||||
// Alice never computes master secret
|
// Alice never derives master key
|
||||||
never(crypto).deriveKey(MASTER_SECRET_LABEL, sharedSecret);
|
never(crypto).deriveKey(MASTER_KEY_LABEL, sharedSecret);
|
||||||
}});
|
}});
|
||||||
|
|
||||||
// execute
|
// execute
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.NoSuchElementException;
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
@@ -28,8 +29,8 @@ public class ConnectionRegistryImplTest extends BrambleTestCase {
|
|||||||
private final TransportId transportId, transportId1;
|
private final TransportId transportId, transportId1;
|
||||||
|
|
||||||
public ConnectionRegistryImplTest() {
|
public ConnectionRegistryImplTest() {
|
||||||
contactId = new ContactId(1);
|
contactId = getContactId();
|
||||||
contactId1 = new ContactId(2);
|
contactId1 = getContactId();
|
||||||
transportId = getTransportId();
|
transportId = getTransportId();
|
||||||
transportId1 = getTransportId();
|
transportId1 = getTransportId();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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.ContactStatusChangedEvent;
|
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
|
||||||
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
import org.briarproject.bramble.api.plugin.ConnectionManager;
|
||||||
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
import org.briarproject.bramble.api.plugin.ConnectionRegistry;
|
||||||
import org.briarproject.bramble.api.plugin.Plugin;
|
import org.briarproject.bramble.api.plugin.Plugin;
|
||||||
@@ -36,6 +36,7 @@ import static java.util.Collections.emptyList;
|
|||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static java.util.Collections.singletonMap;
|
import static java.util.Collections.singletonMap;
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
import static org.briarproject.bramble.test.TestUtils.getTransportId;
|
||||||
|
|
||||||
public class PollerTest extends BrambleMockTestCase {
|
public class PollerTest extends BrambleMockTestCase {
|
||||||
@@ -56,7 +57,7 @@ public class PollerTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
private final Executor ioExecutor = new ImmediateExecutor();
|
private final Executor ioExecutor = new ImmediateExecutor();
|
||||||
private final TransportId transportId = getTransportId();
|
private final TransportId transportId = getTransportId();
|
||||||
private final ContactId contactId = new ContactId(234);
|
private final ContactId contactId = getContactId();
|
||||||
private final TransportProperties properties = new TransportProperties();
|
private final TransportProperties properties = new TransportProperties();
|
||||||
private final int pollingInterval = 60 * 1000;
|
private final int pollingInterval = 60 * 1000;
|
||||||
private final long now = System.currentTimeMillis();
|
private final long now = System.currentTimeMillis();
|
||||||
@@ -67,7 +68,7 @@ public class PollerTest extends BrambleMockTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testConnectOnContactStatusChanged() throws Exception {
|
public void testConnectOnContactAdded() throws Exception {
|
||||||
// Two simplex plugins: one supports polling, the other doesn't
|
// Two simplex plugins: one supports polling, the other doesn't
|
||||||
SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class);
|
SimplexPlugin simplexPlugin = context.mock(SimplexPlugin.class);
|
||||||
SimplexPlugin simplexPlugin1 =
|
SimplexPlugin simplexPlugin1 =
|
||||||
@@ -143,7 +144,7 @@ public class PollerTest extends BrambleMockTestCase {
|
|||||||
connectionRegistry, pluginManager, transportPropertyManager,
|
connectionRegistry, pluginManager, transportPropertyManager,
|
||||||
random, clock);
|
random, clock);
|
||||||
|
|
||||||
p.eventOccurred(new ContactStatusChangedEvent(contactId, true));
|
p.eventOccurred(new ContactAddedEvent(contactId));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import org.briarproject.bramble.api.data.MetadataParser;
|
|||||||
import org.briarproject.bramble.api.db.DatabaseComponent;
|
import org.briarproject.bramble.api.db.DatabaseComponent;
|
||||||
import org.briarproject.bramble.api.db.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.IdentityManager;
|
||||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||||
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;
|
||||||
@@ -36,12 +37,11 @@ import static java.util.Collections.singletonMap;
|
|||||||
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
|
import static org.briarproject.bramble.api.properties.TransportPropertyManager.CLIENT_ID;
|
||||||
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
|
import static org.briarproject.bramble.api.properties.TransportPropertyManager.MAJOR_VERSION;
|
||||||
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
import static org.briarproject.bramble.api.sync.Group.Visibility.SHARED;
|
||||||
import static org.briarproject.bramble.test.TestUtils.getAuthor;
|
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.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;
|
||||||
|
|
||||||
@@ -49,6 +49,8 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
|
||||||
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
|
private final ClientHelper clientHelper = context.mock(ClientHelper.class);
|
||||||
|
private final IdentityManager identityManager =
|
||||||
|
context.mock(IdentityManager.class);
|
||||||
private final ClientVersioningManager clientVersioningManager =
|
private final ClientVersioningManager clientVersioningManager =
|
||||||
context.mock(ClientVersioningManager.class);
|
context.mock(ClientVersioningManager.class);
|
||||||
private final MetadataParser metadataParser =
|
private final MetadataParser metadataParser =
|
||||||
@@ -69,8 +71,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
);
|
);
|
||||||
private final TransportProperties fooProperties, barProperties;
|
private final TransportProperties fooProperties, barProperties;
|
||||||
|
|
||||||
private int nextContactId = 0;
|
|
||||||
|
|
||||||
public TransportPropertyManagerImplTest() throws Exception {
|
public TransportPropertyManagerImplTest() throws Exception {
|
||||||
fooProperties = new TransportProperties();
|
fooProperties = new TransportProperties();
|
||||||
for (String key : fooPropertiesDict.keySet())
|
for (String key : fooPropertiesDict.keySet())
|
||||||
@@ -87,24 +87,26 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
will(returnValue(localGroup));
|
will(returnValue(localGroup));
|
||||||
}});
|
}});
|
||||||
return new TransportPropertyManagerImpl(db, clientHelper,
|
return new TransportPropertyManagerImpl(db, clientHelper,
|
||||||
clientVersioningManager, metadataParser, contactGroupFactory,
|
identityManager, clientVersioningManager, metadataParser,
|
||||||
clock);
|
contactGroupFactory, clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testCreatesGroupsAtStartup() throws Exception {
|
public void testCreatesGroupsAtStartup() throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
Contact contact = getContact(true);
|
Contact contact = getContact();
|
||||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(db).containsGroup(txn, localGroup.getId());
|
oneOf(db).containsGroup(txn, localGroup.getId());
|
||||||
will(returnValue(false));
|
will(returnValue(false));
|
||||||
oneOf(db).addGroup(txn, localGroup);
|
oneOf(db).addGroup(txn, localGroup);
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
oneOf(db).getContacts(txn);
|
oneOf(db).getContacts(txn);
|
||||||
will(returnValue(singletonList(contact)));
|
will(returnValue(singletonList(contact)));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
MAJOR_VERSION, contact);
|
MAJOR_VERSION, contact, localAuthor.getId());
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(db).addGroup(txn, contactGroup);
|
oneOf(db).addGroup(txn, contactGroup);
|
||||||
oneOf(clientVersioningManager).getClientVisibility(txn,
|
oneOf(clientVersioningManager).getClientVisibility(txn,
|
||||||
@@ -141,13 +143,15 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testCreatesContactGroupWhenAddingContact() throws Exception {
|
public void testCreatesContactGroupWhenAddingContact() throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
Contact contact = getContact(true);
|
Contact contact = getContact();
|
||||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
// Create the group and share it with the contact
|
// Create the group and share it with the contact
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
MAJOR_VERSION, contact);
|
MAJOR_VERSION, contact, localAuthor.getId());
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(db).addGroup(txn, contactGroup);
|
oneOf(db).addGroup(txn, contactGroup);
|
||||||
oneOf(clientVersioningManager).getClientVisibility(txn,
|
oneOf(clientVersioningManager).getClientVisibility(txn,
|
||||||
@@ -170,12 +174,14 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testRemovesGroupWhenRemovingContact() throws Exception {
|
public void testRemovesGroupWhenRemovingContact() throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
Contact contact = getContact(true);
|
Contact contact = getContact();
|
||||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
|
||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
MAJOR_VERSION, contact);
|
MAJOR_VERSION, contact, localAuthor.getId());
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(db).removeGroup(txn, contactGroup);
|
oneOf(db).removeGroup(txn, contactGroup);
|
||||||
}});
|
}});
|
||||||
@@ -302,7 +308,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStoresRemotePropertiesWithVersion0() throws Exception {
|
public void testStoresRemotePropertiesWithVersion0() throws Exception {
|
||||||
Contact contact = getContact(true);
|
Contact contact = getContact();
|
||||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
Map<TransportId, TransportProperties> properties =
|
Map<TransportId, TransportProperties> properties =
|
||||||
@@ -313,8 +319,10 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
context.checking(new Expectations() {{
|
context.checking(new Expectations() {{
|
||||||
oneOf(db).getContact(txn, contact.getId());
|
oneOf(db).getContact(txn, contact.getId());
|
||||||
will(returnValue(contact));
|
will(returnValue(contact));
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
MAJOR_VERSION, contact);
|
MAJOR_VERSION, contact, localAuthor.getId());
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
}});
|
}});
|
||||||
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
|
expectStoreMessage(txn, contactGroup.getId(), "foo", fooPropertiesDict,
|
||||||
@@ -406,31 +414,30 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
public void testReturnsRemotePropertiesOrEmptyProperties()
|
public void testReturnsRemotePropertiesOrEmptyProperties()
|
||||||
throws Exception {
|
throws Exception {
|
||||||
Transaction txn = new Transaction(null, true);
|
Transaction txn = new Transaction(null, true);
|
||||||
Contact contact1 = getContact(false);
|
Contact contact1 = getContact();
|
||||||
Contact contact2 = getContact(true);
|
Contact contact2 = getContact();
|
||||||
Contact contact3 = getContact(true);
|
List<Contact> contacts = asList(contact1, contact2);
|
||||||
List<Contact> contacts = asList(contact1, contact2, contact3);
|
Group contactGroup1 = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION);
|
Group contactGroup2 = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
Group contactGroup3 = getGroup(CLIENT_ID, MAJOR_VERSION);
|
Map<MessageId, BdfDictionary> messageMetadata2 =
|
||||||
Map<MessageId, BdfDictionary> messageMetadata3 =
|
|
||||||
new LinkedHashMap<>();
|
new LinkedHashMap<>();
|
||||||
// A remote update for another transport should be ignored
|
// A remote update for another transport should be ignored
|
||||||
MessageId barUpdateId = new MessageId(getRandomId());
|
MessageId barUpdateId = new MessageId(getRandomId());
|
||||||
messageMetadata3.put(barUpdateId, BdfDictionary.of(
|
messageMetadata2.put(barUpdateId, BdfDictionary.of(
|
||||||
new BdfEntry("transportId", "bar"),
|
new BdfEntry("transportId", "bar"),
|
||||||
new BdfEntry("version", 1),
|
new BdfEntry("version", 1),
|
||||||
new BdfEntry("local", false)
|
new BdfEntry("local", false)
|
||||||
));
|
));
|
||||||
// A local update for the right transport should be ignored
|
// A local update for the right transport should be ignored
|
||||||
MessageId localUpdateId = new MessageId(getRandomId());
|
MessageId localUpdateId = new MessageId(getRandomId());
|
||||||
messageMetadata3.put(localUpdateId, BdfDictionary.of(
|
messageMetadata2.put(localUpdateId, BdfDictionary.of(
|
||||||
new BdfEntry("transportId", "foo"),
|
new BdfEntry("transportId", "foo"),
|
||||||
new BdfEntry("version", 1),
|
new BdfEntry("version", 1),
|
||||||
new BdfEntry("local", true)
|
new BdfEntry("local", true)
|
||||||
));
|
));
|
||||||
// A remote update for the right transport should be returned
|
// A remote update for the right transport should be returned
|
||||||
MessageId fooUpdateId = new MessageId(getRandomId());
|
MessageId fooUpdateId = new MessageId(getRandomId());
|
||||||
messageMetadata3.put(fooUpdateId, BdfDictionary.of(
|
messageMetadata2.put(fooUpdateId, BdfDictionary.of(
|
||||||
new BdfEntry("transportId", "foo"),
|
new BdfEntry("transportId", "foo"),
|
||||||
new BdfEntry("version", 1),
|
new BdfEntry("version", 1),
|
||||||
new BdfEntry("local", false)
|
new BdfEntry("local", false)
|
||||||
@@ -439,23 +446,24 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
|
|
||||||
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);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
oneOf(db).getContacts(txn);
|
oneOf(db).getContacts(txn);
|
||||||
will(returnValue(contacts));
|
will(returnValue(contacts));
|
||||||
// First contact: skipped because not active
|
// First contact: no updates
|
||||||
// Second contact: no updates
|
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
MAJOR_VERSION, contact2);
|
MAJOR_VERSION, contact1, localAuthor.getId());
|
||||||
|
will(returnValue(contactGroup1));
|
||||||
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
|
contactGroup1.getId());
|
||||||
|
will(returnValue(Collections.emptyMap()));
|
||||||
|
// Second contact: returns an update
|
||||||
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
|
MAJOR_VERSION, contact2, localAuthor.getId());
|
||||||
will(returnValue(contactGroup2));
|
will(returnValue(contactGroup2));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup2.getId());
|
contactGroup2.getId());
|
||||||
will(returnValue(Collections.emptyMap()));
|
will(returnValue(messageMetadata2));
|
||||||
// Third contact: returns an update
|
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
|
||||||
MAJOR_VERSION, contact3);
|
|
||||||
will(returnValue(contactGroup3));
|
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
|
||||||
contactGroup3.getId());
|
|
||||||
will(returnValue(messageMetadata3));
|
|
||||||
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
oneOf(clientHelper).getMessageAsList(txn, fooUpdateId);
|
||||||
will(returnValue(fooUpdate));
|
will(returnValue(fooUpdate));
|
||||||
oneOf(clientHelper).parseAndValidateTransportProperties(
|
oneOf(clientHelper).parseAndValidateTransportProperties(
|
||||||
@@ -466,10 +474,9 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
TransportPropertyManagerImpl t = createInstance();
|
TransportPropertyManagerImpl t = createInstance();
|
||||||
Map<ContactId, TransportProperties> properties =
|
Map<ContactId, TransportProperties> properties =
|
||||||
t.getRemoteProperties(new TransportId("foo"));
|
t.getRemoteProperties(new TransportId("foo"));
|
||||||
assertEquals(3, properties.size());
|
assertEquals(2, properties.size());
|
||||||
assertEquals(0, properties.get(contact1.getId()).size());
|
assertEquals(0, properties.get(contact1.getId()).size());
|
||||||
assertEquals(0, properties.get(contact2.getId()).size());
|
assertEquals(fooProperties, properties.get(contact2.getId()));
|
||||||
assertEquals(fooProperties, properties.get(contact3.getId()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -506,7 +513,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testMergingNewPropertiesCreatesUpdate() throws Exception {
|
public void testMergingNewPropertiesCreatesUpdate() throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
Contact contact = getContact(true);
|
Contact contact = getContact();
|
||||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
|
|
||||||
context.checking(new DbExpectations() {{
|
context.checking(new DbExpectations() {{
|
||||||
@@ -519,10 +526,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
expectStoreMessage(txn, localGroup.getId(), "foo",
|
expectStoreMessage(txn, localGroup.getId(), "foo",
|
||||||
fooPropertiesDict, 1, true, false);
|
fooPropertiesDict, 1, true, false);
|
||||||
// Store the new properties in each contact's group, version 1
|
// Store the new properties in each contact's group, version 1
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
oneOf(db).getContacts(txn);
|
oneOf(db).getContacts(txn);
|
||||||
will(returnValue(singletonList(contact)));
|
will(returnValue(singletonList(contact)));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
MAJOR_VERSION, contact);
|
MAJOR_VERSION, contact, localAuthor.getId());
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup.getId());
|
contactGroup.getId());
|
||||||
@@ -538,7 +547,7 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
@Test
|
@Test
|
||||||
public void testMergingUpdatedPropertiesCreatesUpdate() throws Exception {
|
public void testMergingUpdatedPropertiesCreatesUpdate() throws Exception {
|
||||||
Transaction txn = new Transaction(null, false);
|
Transaction txn = new Transaction(null, false);
|
||||||
Contact contact = getContact(true);
|
Contact contact = getContact();
|
||||||
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
Group contactGroup = getGroup(CLIENT_ID, MAJOR_VERSION);
|
||||||
BdfDictionary oldMetadata = BdfDictionary.of(
|
BdfDictionary oldMetadata = BdfDictionary.of(
|
||||||
new BdfEntry("transportId", "foo"),
|
new BdfEntry("transportId", "foo"),
|
||||||
@@ -575,10 +584,12 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
// Delete the previous update
|
// Delete the previous update
|
||||||
oneOf(db).removeMessage(txn, localGroupUpdateId);
|
oneOf(db).removeMessage(txn, localGroupUpdateId);
|
||||||
// Store the merged properties in each contact's group, version 2
|
// Store the merged properties in each contact's group, version 2
|
||||||
|
oneOf(identityManager).getLocalAuthor(txn);
|
||||||
|
will(returnValue(localAuthor));
|
||||||
oneOf(db).getContacts(txn);
|
oneOf(db).getContacts(txn);
|
||||||
will(returnValue(singletonList(contact)));
|
will(returnValue(singletonList(contact)));
|
||||||
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
oneOf(contactGroupFactory).createContactGroup(CLIENT_ID,
|
||||||
MAJOR_VERSION, contact);
|
MAJOR_VERSION, contact, localAuthor.getId());
|
||||||
will(returnValue(contactGroup));
|
will(returnValue(contactGroup));
|
||||||
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
oneOf(clientHelper).getMessageMetadataAsDictionary(txn,
|
||||||
contactGroup.getId());
|
contactGroup.getId());
|
||||||
@@ -593,12 +604,6 @@ public class TransportPropertyManagerImplTest extends BrambleMockTestCase {
|
|||||||
t.mergeLocalProperties(new TransportId("foo"), fooProperties);
|
t.mergeLocalProperties(new TransportId("foo"), fooProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Contact getContact(boolean active) {
|
|
||||||
ContactId c = new ContactId(nextContactId++);
|
|
||||||
return new Contact(c, getAuthor(), localAuthor.getId(),
|
|
||||||
getRandomString(5), true, active);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void expectGetLocalProperties(Transaction txn) throws Exception {
|
private void expectGetLocalProperties(Transaction txn) throws Exception {
|
||||||
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
Map<MessageId, BdfDictionary> messageMetadata = new LinkedHashMap<>();
|
||||||
// The latest update for transport "foo" should be returned
|
// The latest update for transport "foo" should be returned
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import java.util.concurrent.Executor;
|
|||||||
|
|
||||||
import static java.util.Collections.singletonList;
|
import static java.util.Collections.singletonList;
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_MESSAGE_IDS;
|
||||||
|
import static org.briarproject.bramble.test.TestUtils.getContactId;
|
||||||
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;
|
||||||
|
|
||||||
@@ -33,7 +34,7 @@ public class SimplexOutgoingSessionTest extends BrambleMockTestCase {
|
|||||||
context.mock(SyncRecordWriter.class);
|
context.mock(SyncRecordWriter.class);
|
||||||
|
|
||||||
private final Executor dbExecutor = new ImmediateExecutor();
|
private final Executor dbExecutor = new ImmediateExecutor();
|
||||||
private final ContactId contactId = new ContactId(234);
|
private final ContactId contactId = getContactId();
|
||||||
private final Message message = getMessage(new GroupId(getRandomId()));
|
private final Message message = getMessage(new GroupId(getRandomId()));
|
||||||
private final MessageId messageId = message.getId();
|
private final MessageId messageId = message.getId();
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user