Compare commits

..

2 Commits

Author SHA1 Message Date
akwizgran
33bfef12e4 WIP: Store descriptor in metadata so invite can be deleted. 2018-11-27 12:44:45 +00:00
akwizgran
92163d6a99 WIP: Test effect of deleting private messages on delivery. 2018-11-27 10:07:16 +00:00
444 changed files with 5385 additions and 14290 deletions

View File

@@ -12,7 +12,7 @@ test:
script:
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom test
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom test --continue
after_script:
# these file change every time but should not be cached

View File

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

View File

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

View File

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

View File

@@ -18,8 +18,8 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.IoUtils;
import java.io.Closeable;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.ArrayList;
@@ -51,6 +51,7 @@ import static android.bluetooth.BluetoothDevice.EXTRA_DEVICE;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubMacAddress;
@MethodsNotNullByDefault
@@ -160,7 +161,11 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
@Override
void tryToClose(@Nullable BluetoothServerSocket ss) {
IoUtils.tryToClose(ss, LOG, WARNING);
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
@Override
@@ -190,7 +195,7 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
s.connect();
return wrapSocket(s);
} catch (IOException e) {
IoUtils.tryToClose(s, LOG, WARNING);
tryToClose(s);
throw e;
}
}
@@ -263,6 +268,14 @@ class AndroidBluetoothPlugin extends BluetoothPlugin<BluetoothServerSocket> {
return addresses;
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private class BluetoothStateReceiver extends BroadcastReceiver {
@Override

View File

@@ -32,6 +32,9 @@ import static java.util.concurrent.TimeUnit.MINUTES;
@ParametersNotNullByDefault
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 RenewableWakeLock wakeLock;
@@ -52,7 +55,7 @@ class AndroidTorPlugin extends TorPlugin {
appContext.getSystemService(POWER_SERVICE);
if (pm == null) throw new AssertionError();
wakeLock = new RenewableWakeLock(pm, scheduler, PARTIAL_WAKE_LOCK,
getWakeLockTag(), 1, MINUTES);
WAKE_LOCK_TAG, 1, MINUTES);
}
@Override
@@ -84,17 +87,4 @@ class AndroidTorPlugin extends TorPlugin {
super.stop();
wakeLock.release();
}
private String getWakeLockTag() {
PackageManager pm = appContext.getPackageManager();
for (PackageInfo info : pm.getInstalledPackages(0)) {
String name = info.packageName.toLowerCase();
if (name.startsWith("com.huawei.powergenie")) {
return "LocationManagerService";
} else if (name.startsWith("com.evenwell.powermonitor")) {
return "AudioIn";
}
}
return getClass().getSimpleName();
}
}

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.system;
import android.annotation.SuppressLint;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -27,7 +26,7 @@ import static android.provider.Settings.Secure.ANDROID_ID;
@Immutable
@NotNullByDefault
class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
class AndroidSecureRandomProvider extends LinuxSecureRandomProvider {
private static final int SEED_LENGTH = 32;
@@ -38,7 +37,6 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
appContext = app.getApplicationContext();
}
@SuppressLint("HardwareIds")
@Override
protected void writeToEntropyPool(DataOutputStream out) throws IOException {
super.writeToEntropyPool(out);
@@ -51,14 +49,12 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
String id = Settings.Secure.getString(contentResolver, ANDROID_ID);
if (id != null) out.writeUTF(id);
Parcel parcel = Parcel.obtain();
WifiManager wm = (WifiManager) appContext.getApplicationContext()
.getSystemService(WIFI_SERVICE);
if (wm != null) {
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
if (configs != null) {
for (WifiConfiguration config : configs)
parcel.writeParcelable(config, 0);
}
WifiManager wm =
(WifiManager) appContext.getSystemService(WIFI_SERVICE);
List<WifiConfiguration> configs = wm.getConfiguredNetworks();
if (configs != null) {
for (WifiConfiguration config : configs)
parcel.writeParcelable(config, 0);
}
BluetoothAdapter bt = BluetoothAdapter.getDefaultAdapter();
if (bt != null) {
@@ -81,13 +77,13 @@ class AndroidSecureRandomProvider extends UnixSecureRandomProvider {
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private void applyOpenSslFix() {
byte[] seed = new UnixSecureRandomSpi().engineGenerateSeed(
byte[] seed = new LinuxSecureRandomSpi().engineGenerateSeed(
SEED_LENGTH);
try {
// Seed the OpenSSL PRNG
Class.forName("org.apache.harmony.xnet.provider.jsse.NativeCrypto")
.getMethod("RAND_seed", byte[].class)
.invoke(null, (Object) seed);
.invoke(null, seed);
// Mix the output of the Linux PRNG into the OpenSSL PRNG
int bytesRead = (Integer) Class.forName(
"org.apache.harmony.xnet.provider.jsse.NativeCrypto")

View File

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

View File

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

View File

@@ -1,44 +1,46 @@
dependencyVerification {
verify = [
'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.android.tools.analytics-library:protos:26.3.2:protos-26.3.2.jar:50238fb4298b297217b184b9cd93c14f83536fcee829eb0ca850bdb5b53251f0',
'com.android.tools.analytics-library:shared:26.3.2:shared-26.3.2.jar:ddd80dcf21905018b7c0583ba72b7282f446084d4952422609a09fbf8237ef71',
'com.android.tools.analytics-library:tracker:26.3.2:tracker-26.3.2.jar:28c575d2d1af003e96d7b375a668ee10b2673a2dd0f6438750aa8c3b42e7d0ad',
'com.android.tools.build:apksig:3.3.2:apksig-3.3.2.jar:84c4aaa20127c6c1fe6bdd334b3f5df71f54ad080be9029c8a10f43b6a908acd',
'com.android.tools.build:apkzlib:3.3.2:apkzlib-3.3.2.jar:d34e523278e5dff565eba3ef3c089d515b2b5cc7b47dc77e2f3465e5e47176ac',
'com.android.tools.build:builder-model:3.3.2:builder-model-3.3.2.jar:055e3db0ecee9e06b9f024034999a29cd92cb1885207b37542126bd8bcc57f46',
'com.android.tools.build:builder-test-api:3.3.2:builder-test-api-3.3.2.jar:0b2e4cd7615bbcad14a3c91fe45ae26693508d06e40ba06c5968b8bc24416618',
'com.android.tools.build:builder:3.3.2:builder-3.3.2.jar:65649704da7aef0487235fd326f0f2e99ed5cf958e80f204496e6e08a42bd9f5',
'com.android.tools.build:gradle-api:3.3.2:gradle-api-3.3.2.jar:3cbd47e41bb70330dd72ec2c9fe51e6173554b484a03829b5a2de9e00841e040',
'com.android.tools.build:manifest-merger:26.3.2:manifest-merger-26.3.2.jar:05c4a6d8b02fb9f08744876477d0a68547c03a8a9069b1f086684fa04af97c33',
'com.android.tools.ddms:ddmlib:26.3.2:ddmlib-26.3.2.jar:d248da8a563d6e46d2c7ebbf371a4877e00510f4ca763c0bb272d5a281bf8b85',
'com.android.tools.external.com-intellij:intellij-core:26.3.2:intellij-core-26.3.2.jar:6c5ecc968230e9f4dcd0fef28885379feace1f0cd8130de6f61d649c86139bf3',
'com.android.tools.external.com-intellij:kotlin-compiler:26.3.2:kotlin-compiler-26.3.2.jar:1007d9b07ccb49cd8eaf30fda10ed4681d4714f2f9ab2ecda39b4e539cc51bbe',
'com.android.tools.external.org-jetbrains:uast:26.3.2:uast-26.3.2.jar:5d1833e562ea4f38a89708dfde695f0a162cbd39d003d3dde818c3fdc2b05317',
'com.android.tools.layoutlib:layoutlib-api:26.3.2:layoutlib-api-26.3.2.jar:d7e61e874ab95f5c350dd38b6a95b5c9dbe0083a02001884264cdb390cb255b8',
'com.android.tools.lint:lint-api:26.3.2:lint-api-26.3.2.jar:5867dfd7fb4a4e161a816a5d29d045f9b542d34594c00a1efec46fb4cd0e1033',
'com.android.tools.lint:lint-checks:26.3.2:lint-checks-26.3.2.jar:4b163b9c93790d2771e92ba8de58a0d9e0671ffcf2ccef3cf496efd442e27517',
'com.android.tools.lint:lint-gradle-api:26.3.2:lint-gradle-api-26.3.2.jar:54cb282e0c054f9bed3f51302ce08b003c8ab7961dfd5a4f6de26c23cc23062f',
'com.android.tools.lint:lint-gradle:26.3.2:lint-gradle-26.3.2.jar:bb139615f4ce97d42cc394b9389b49b76a6eb85be6785a5d272991543b519013',
'com.android.tools.lint:lint:26.3.2:lint-26.3.2.jar:ef7b369f8a56a92ccb0f4c1c357666b9339e4a711a9d84747d446441746cfe4e',
'com.android.tools:annotations:26.3.2:annotations-26.3.2.jar:5bcce8e98b6a2f5ccf13ebcefd8f734e0b35f8b19e456575665631442ce1f7a1',
'com.android.tools:common:26.3.2:common-26.3.2.jar:d9f8e7f0669e9a701568e3db6a87c89cf12d8fa6811c9991e969f950215ecfac',
'com.android.tools:dvlib:26.3.2:dvlib-26.3.2.jar:d84aad56161c7773579303d69714ded6897c64c6ddfd7d456e453231e4dfe811',
'com.android.tools:repository:26.3.2:repository-26.3.2.jar:da611eeb06e9ab8750d25b9e2901e10db8e5ec6304eb4c8b7103d39e0921ea40',
'com.android.tools:sdk-common:26.3.2:sdk-common-26.3.2.jar:82823a3bf25e64fac33a286490f0cf5ac50c2cdb3c540149b030896bb44bf96c',
'com.android.tools:sdklib:26.3.2:sdklib-26.3.2.jar:424d15492af67321900963238646d27495ab60de2a5b19e6a416963bc5d6932b',
'com.android.tools.analytics-library:protos:26.2.1:protos-26.2.1.jar:2f371f5b1f551e85ab08be4d6a2873471b3d44afd1ebf6aa3298f3b796bf691f',
'com.android.tools.analytics-library:shared:26.2.1:shared-26.2.1.jar:4c1e4e705fa4d45f23aaea230557f6508155012d9c296337787c1d7b26a97f5a',
'com.android.tools.analytics-library:tracker:26.2.1:tracker-26.2.1.jar:4a624ecc976539f755ddb0bb8dfc2dd3d08326cfec59a098dbd70f701ca7fb75',
'com.android.tools.build:aapt2:3.2.1-4818971:aapt2-3.2.1-4818971-linux.jar:f431b6f96c91a2c155144b091a9c97d9805c589fe8efc9c930b6cd346cb60a1e',
'com.android.tools.build:apksig:3.2.1:apksig-3.2.1.jar:2b46f2feffea66037aab29e4261b2433c190194a6ef97b958511eb157f2ccba5',
'com.android.tools.build:apkzlib:3.2.1:apkzlib-3.2.1.jar:c39ad0313905932431fe81c8899c2cf39a4d92ad6c4edcaa4b25432f461452aa',
'com.android.tools.build:builder-model:3.2.1:builder-model-3.2.1.jar:a9f68e6abcec122f9cb5ad352d3f05a3eb03acbcdca95e4d25c16310c2c965ff',
'com.android.tools.build:builder-test-api:3.2.1:builder-test-api-3.2.1.jar:533ac6c2b5884bb54967a33791f2628dfdfac7981af39417a333b43d4379b6be',
'com.android.tools.build:builder:3.2.1:builder-3.2.1.jar:aedcbfd115dbe91d09b4113e66ef50589b558d0aa3b2f133b1d867c9b87fae83',
'com.android.tools.build:gradle-api:3.2.1:gradle-api-3.2.1.jar:57cf0ac5ac1dca8afdb3f62b94265e776e7dcfa641cc3844fb53a05193de208d',
'com.android.tools.build:manifest-merger:26.2.1:manifest-merger-26.2.1.jar:8830573263361035d38cfdcb51e2db94029c93865b21334f5fbf8a27984281a6',
'com.android.tools.ddms:ddmlib:26.2.1:ddmlib-26.2.1.jar:a4bf0a29a19980bf27269465cc782064656750b77c26728f82f9e148b705218b',
'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.2.1:kotlin-compiler-26.2.1.jar:daa064fd708f340ee25fb9823c4c74104ac77f1370b76d907eb9ae6daec0a2ae',
'com.android.tools.external.org-jetbrains:uast:26.2.1:uast-26.2.1.jar:f10f7258d2ab9189562cc0f9ad838c0378fdba439229173390a99de02ebac75b',
'com.android.tools.layoutlib:layoutlib-api:26.2.1:layoutlib-api-26.2.1.jar:ddbf4fca123733fa011595b1cc1f4ac2937ed327b60990711fafc33c775c2ade',
'com.android.tools.lint:lint-api:26.2.1:lint-api-26.2.1.jar:3b57e739de567b98bc9ab56c2c0ee66fc026b4adf5843e8f9804ca0666a6f66e',
'com.android.tools.lint:lint-checks:26.2.1:lint-checks-26.2.1.jar:c86f4cc9aaee722ee4ad70062f7b5af91e9b041914af27adc09f545ab0fb3bc6',
'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.2.1:lint-gradle-26.2.1.jar:8fd90b2f3ec788cbb9801c07ab3e1ea2255aa31a6093157d7ea0ff13d0315ecb',
'com.android.tools.lint:lint-kotlin:26.2.1:lint-kotlin-26.2.1.jar:7a6a5d2b18f69cf1b900d857c2632b4c683713c533295933b8b759f8cab4a877',
'com.android.tools.lint:lint:26.2.1:lint-26.2.1.jar:7848b82ae988b90dee259ae7c7e86e05cbf52db6cd21c8bbd38ce7df08f3f8c5',
'com.android.tools:annotations:26.2.1:annotations-26.2.1.jar:7391c6a1e080174b96e64ceb078dadd31ce4d8a2d2fee0ec65be202126f90f24',
'com.android.tools:common:26.2.1:common-26.2.1.jar:a50aab2d6411ff68f4004a87c7e93d87d8e980a0ec3b352246549897ea2d78e5',
'com.android.tools:dvlib:26.2.1:dvlib-26.2.1.jar:72a83bf2839b1df9b1fbf67ba45d1bfb9f966cd774da4320c762b2be8f1688aa',
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1',
'com.android.tools:sdk-common:26.2.1:sdk-common-26.2.1.jar:759d4b292ca69a35cf961fca377b54158fc6c88108978006999442e80a011cf4',
'com.android.tools:sdklib:26.2.1:sdklib-26.2.1.jar:248df7ad5eac4aeb6f96c394c76760de4b7b89ac056e54d0c21a739368b91b45',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
'com.google.code.findbugs:jsr305: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.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-spi:2.19:dagger-spi-2.19.jar:e7a6379d82c841f6aac2866948ad1eed716528707814602842a8d844ce04e2e1',
'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: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.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:26.0-jre:guava-26.0-jre.jar:a0e9cabad665bc20bcd2b01f108e5fc03f756e13aea80abaadb9f407033bea2c',
'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.protobuf:protobuf-java:3.4.0:protobuf-java-3.4.0.jar:dce7e66b32456a1b1198da0caff3a8acb71548658391e798c79369241e6490a4',
@@ -66,22 +68,21 @@ dependencyVerification {
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:obfs4proxy-android:0.0.9:obfs4proxy-android-0.0.9.zip:9b7e9181535ea8d8bbe8ae6338e08cf4c5fc1e357a779393e0ce49586d459ae0',
'org.briarproject:tor-android:0.3.5.8:tor-android-0.3.5.8.zip:42a13a6f185be1a62f42e3f30ce66a3c099ac5ec890a65e7593111b65b44a54a',
'org.briarproject:obfs4proxy-android:0.0.7:obfs4proxy-android-0.0.7.zip:abdfb5d889d848de9bf214f9276abbf454808a505b870819eccc9a9e985bf617',
'org.briarproject:tor-android:0.3.4.8:tor-android-0.3.4.8.zip:989a0352d9d8d8172cd6c2137654e165e5d2beb10ed1211bab3814e224ad1926',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-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.groovy:groovy-all:2.4.12:groovy-all-2.4.12.jar:6a56af4bd48903d56bec62821876cadefafd007360cc6bd0d8f7aa8d72b38be4',
'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-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.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.jetbrains.kotlin:kotlin-reflect:1.3.21:kotlin-reflect-1.3.21.jar:a3065c822633191e0a3e3ee12a29bec234fc4b2864a6bb87ef48cce3e9e0c26a',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.21:kotlin-stdlib-common-1.3.21.jar:cea61f7b611895e64f58569a9757fc0ab0d582f107211e1930e0ce2a0add52a7',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21:kotlin-stdlib-jdk7-1.3.21.jar:a87875604fd42140da6938ae4d35ee61081f4482536efc6d2615b8b626a198af',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.21:kotlin-stdlib-jdk8-1.3.21.jar:5823ed66ac122a1c55442ebca5a209a843ccd87f562edc31a787f3d2e47f74d4',
'org.jetbrains.kotlin:kotlin-stdlib:1.3.21:kotlin-stdlib-1.3.21.jar:38ba2370d9f06f50433e06b2ca775b94473c2e2785f410926079ab793c72b034',
'org.jetbrains.kotlin:kotlin-reflect:1.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.2.71:kotlin-stdlib-common-1.2.71.jar:63999687ff2fce8a592dd180ffbbf8f1d21c26b4044c55cdc74ff3cf3b3cf328',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.2.71:kotlin-stdlib-jdk7-1.2.71.jar:b136bd61b240e07d4d92ce00d3bd1dbf584400a7bf5f220c2f3cd22446858082',
'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.2.71:kotlin-stdlib-jdk8-1.2.71.jar:ac3c8abf47790b64b4f7e2509a53f0c145e061ac1612a597520535d199946ea9',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.71:kotlin-stdlib-1.2.71.jar:4c895c270b87f5fec2a2796e1d89c15407ee821de961527c28588bb46afbc68b',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,80 +24,61 @@ public interface ContactManager {
void registerContactHook(ContactHook hook);
/**
* Stores a contact with the given pseudonym, derives and stores transport
* keys for each transport, and returns an ID for the contact.
* 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(Transaction txn, Author a, SecretKey rootKey,
ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms
* and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException;
/**
* Stores a contact associated with the given local and remote pseudonyms,
* derives and stores transport keys for each transport, and returns an ID
* for the contact.
*
* @param alice true if the local party is Alice
*/
ContactId addContact(Author remote, AuthorId local, SecretKey master,
long timestamp, boolean alice, boolean verified, boolean active)
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.
*/
Contact getContact(ContactId c) throws DbException;
/**
* Returns the contact with the given ID.
* Returns the contact with the given remoteAuthorId
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws org.briarproject.bramble.api.db.NoSuchContactException
*/
Contact getContact(AuthorId a) throws DbException;
Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException;
/**
* Returns the contact with the given ID.
* Returns the contact with the given remoteAuthorId
* that was added by the LocalAuthor with the given localAuthorId
*
* @throws org.briarproject.bramble.api.db.NoSuchContactException
*/
Contact getContact(Transaction txn, AuthorId a) throws DbException;
Contact getContact(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException;
/**
* Returns all active contacts.
*/
Collection<Contact> getContacts() throws DbException;
Collection<Contact> getActiveContacts() throws DbException;
/**
* Removes a contact and all associated state.
@@ -109,6 +90,12 @@ public interface ContactManager {
*/
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.
*/
@@ -122,14 +109,16 @@ public interface ContactManager {
throws DbException;
/**
* Returns true if a contact with this pseudonym already exists.
* Return true if a contact with this name and public key already exists
*/
boolean contactExists(Transaction txn, AuthorId a) throws DbException;
boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException;
/**
* Returns true if a contact with this pseudonym already exists.
* Return true if a contact with this name and public key already exists
*/
boolean contactExists(AuthorId a) throws DbException;
boolean contactExists(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException;
/**
* Returns the {@link AuthorInfo} for the given author.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,8 +2,6 @@ package org.briarproject.bramble.api.db;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.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.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
@@ -22,11 +20,8 @@ import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.Offer;
import org.briarproject.bramble.api.sync.Request;
import org.briarproject.bramble.api.sync.validation.MessageState;
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.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection;
@@ -102,31 +97,17 @@ public interface DatabaseComponent {
NullableDbCallable<R, E> task) throws DbException, E;
/**
* Stores a contact with the given pseudonym and returns an ID for the
* contact.
* Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact.
*/
ContactId addContact(Transaction txn, Author a, boolean verified)
throws DbException;
ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException;
/**
* Stores a group.
*/
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.
*/
@@ -138,12 +119,6 @@ public interface DatabaseComponent {
void addLocalMessage(Transaction txn, Message m, Metadata meta,
boolean shared) throws DbException;
/**
* Stores a pending contact.
*/
void addPendingContact(Transaction txn, PendingContact p)
throws DbException;
/**
* Stores a transport.
*/
@@ -154,39 +129,27 @@ public interface DatabaseComponent {
* Stores the given transport keys for the given contact and returns a
* key set ID.
*/
TransportKeySetId addTransportKeys(Transaction txn, ContactId c,
KeySetId addTransportKeys(Transaction txn, ContactId c,
TransportKeys k) throws DbException;
/**
* Returns true if the database contains the given contact.
* <p/>
* Read-only.
* Returns true if the database contains the given contact for the given
* local pseudonym.
*/
boolean containsContact(Transaction txn, AuthorId a) throws DbException;
boolean containsContact(Transaction txn, AuthorId remote, AuthorId local)
throws DbException;
/**
* Returns true if the database contains the given group.
* <p/>
* Read-only.
*/
boolean containsGroup(Transaction txn, GroupId g) throws DbException;
/**
* Returns true if the database contains the given local author.
* <p/>
* Read-only.
*/
boolean containsLocalAuthor(Transaction txn, AuthorId local)
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
* {@link #removeMessage(Transaction, MessageId)}, the message ID,
@@ -252,13 +215,6 @@ public interface DatabaseComponent {
*/
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.
* <p/>
@@ -266,6 +222,22 @@ public interface DatabaseComponent {
*/
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.
* <p/>
@@ -297,14 +269,6 @@ public interface DatabaseComponent {
Visibility getGroupVisibility(Transaction txn, ContactId c, GroupId g)
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.
* <p/>
@@ -453,14 +417,6 @@ public interface DatabaseComponent {
*/
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.
* <p/>
@@ -473,20 +429,14 @@ public interface DatabaseComponent {
* <p/>
* Read-only.
*/
Collection<TransportKeySet> getTransportKeys(Transaction txn, TransportId t)
Collection<KeySet> getTransportKeys(Transaction txn, TransportId t)
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.
*/
void incrementStreamCounter(Transaction txn, TransportId t,
TransportKeySetId k) throws DbException;
void incrementStreamCounter(Transaction txn, TransportId t, KeySetId k)
throws DbException;
/**
* Merges the given metadata with the existing metadata for the given
@@ -541,12 +491,6 @@ public interface DatabaseComponent {
*/
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.
*/
@@ -557,12 +501,6 @@ public interface DatabaseComponent {
*/
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.
*/
@@ -571,14 +509,20 @@ public interface DatabaseComponent {
/**
* Removes the given transport keys from the database.
*/
void removeTransportKeys(Transaction txn, TransportId t,
TransportKeySetId k) throws DbException;
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
throws DbException;
/**
* Marks the given contact as verified.
*/
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.
*/
@@ -609,36 +553,21 @@ public interface DatabaseComponent {
Collection<MessageId> dependencies) throws DbException;
/**
* Sets the reordering window for the given transport key set in the given
* time period.
* Sets the reordering window for the given key set and transport in the
* given rotation period.
*/
void setReorderingWindow(Transaction 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(Transaction txn, HandshakeKeySetId k,
TransportId t, long timePeriod, long base, byte[] bitmap)
throws DbException;
void setReorderingWindow(Transaction txn, KeySetId k, TransportId t,
long rotationPeriod, long base, byte[] bitmap) throws DbException;
/**
* Marks the given transport keys as usable for outgoing streams.
*/
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)
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
throws DbException;
/**
* Stores the given transport keys, deleting any keys they have replaced.
*/
void updateTransportKeys(Transaction txn, Collection<TransportKeySet> keys)
void updateTransportKeys(Transaction txn, Collection<KeySet> keys)
throws DbException;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,32 +1,27 @@
package org.briarproject.bramble.api.transport;
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
* transport in a given time period.
* transport in a given rotation period.
*/
@Immutable
@NotNullByDefault
public class OutgoingKeys {
private final SecretKey tagKey, headerKey;
private final long timePeriod, streamCounter;
private final long rotationPeriod, streamCounter;
private final boolean active;
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long timePeriod, boolean active) {
this(tagKey, headerKey, timePeriod, 0, active);
long rotationPeriod, boolean active) {
this(tagKey, headerKey, rotationPeriod, 0, active);
}
public OutgoingKeys(SecretKey tagKey, SecretKey headerKey,
long timePeriod, long streamCounter, boolean active) {
long rotationPeriod, long streamCounter, boolean active) {
this.tagKey = tagKey;
this.headerKey = headerKey;
this.timePeriod = timePeriod;
this.rotationPeriod = rotationPeriod;
this.streamCounter = streamCounter;
this.active = active;
}
@@ -39,8 +34,8 @@ public class OutgoingKeys {
return headerKey;
}
public long getTimePeriod() {
return timePeriod;
public long getRotationPeriod() {
return rotationPeriod;
}
public long getStreamCounter() {

View File

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

View File

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

View File

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

View File

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

View File

@@ -38,13 +38,6 @@ public interface ClientVersioningManager {
Visibility getClientVisibility(Transaction txn, ContactId contactId,
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 {
void onClientVisibilityChanging(Transaction txn, Contact c,

View File

@@ -8,15 +8,12 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
public class IoUtils {
@@ -57,35 +54,16 @@ public class IoUtils {
out.flush();
out.close();
} catch (IOException e) {
tryToClose(in, LOG, WARNING);
tryToClose(out, LOG, WARNING);
tryToClose(in);
tryToClose(out);
}
}
public static void tryToClose(@Nullable Closeable c, Logger logger,
Level level) {
private static void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(logger, level, e);
}
}
public static void tryToClose(@Nullable Socket s, Logger logger,
Level level) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(logger, level, e);
}
}
public static void tryToClose(@Nullable ServerSocket ss, Logger logger,
Level level) {
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(logger, level, e);
// We did our best
}
}

View File

@@ -10,6 +10,8 @@ public class OsUtils {
@Nullable
private static final String os = System.getProperty("os.name");
@Nullable
private static final String version = System.getProperty("os.version");
@Nullable
private static final String vendor = System.getProperty("java.vendor");
public static boolean isWindows() {

View File

@@ -1,10 +1,6 @@
package org.briarproject.bramble.test;
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.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
@@ -29,7 +25,6 @@ import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
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.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
@@ -46,7 +41,6 @@ public class TestUtils {
new AtomicInteger((int) (Math.random() * 1000 * 1000));
private static final Random random = new Random();
private static final long timestamp = System.currentTimeMillis();
private static final AtomicInteger nextContactId = new AtomicInteger(1);
public static File getTestDirectory() {
int name = nextTestDir.getAndIncrement();
@@ -146,35 +140,6 @@ public class TestUtils {
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) {
int size = samples.size();
if (size == 0) throw new IllegalArgumentException();

View File

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

View File

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

View File

@@ -3,11 +3,10 @@ package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
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.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.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
@@ -17,20 +16,16 @@ import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.transport.KeyManager;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
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_PUBLIC_KEY_LENGTH;
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.UNVERIFIED;
@@ -41,12 +36,6 @@ import static org.briarproject.bramble.util.StringUtils.toUtf8;
@NotNullByDefault
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 KeyManager keyManager;
private final IdentityManager identityManager;
@@ -67,94 +56,66 @@ class ContactManagerImpl implements ContactManager {
}
@Override
public ContactId addContact(Transaction txn, Author a, SecretKey rootKey,
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
SecretKey master, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
keyManager.addContact(txn, c, master, timestamp, alice, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
boolean verified, boolean active) throws DbException {
ContactId c = db.addContact(txn, remote, local, verified, active);
Contact contact = db.getContact(txn, c);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override
public ContactId addContact(Author remote, AuthorId local, SecretKey master,
long timestamp, boolean alice, boolean verified, boolean active)
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 ->
addContact(txn, a, rootKey, timestamp, alice,
addContact(txn, remote, local, master, timestamp, alice,
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
public Contact getContact(ContactId c) throws DbException {
return db.transactionWithResult(true, txn -> db.getContact(txn, c));
}
@Override
public Contact getContact(AuthorId a) throws DbException {
return db.transactionWithResult(true, txn -> getContact(txn, a));
public Contact getContact(AuthorId remoteAuthorId, AuthorId localAuthorId)
throws DbException {
return db.transactionWithResult(true, txn ->
getContact(txn, remoteAuthorId, localAuthorId));
}
@Override
public Contact getContact(Transaction txn, AuthorId a) throws DbException {
return db.getContact(txn, a);
public Contact getContact(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
Collection<Contact> contacts =
db.getContactsByAuthorId(txn, remoteAuthorId);
for (Contact c : contacts) {
if (c.getLocalAuthorId().equals(localAuthorId)) {
return c;
}
}
throw new NoSuchContactException();
}
@Override
public Collection<Contact> getContacts() throws DbException {
return db.transactionWithResult(true, db::getContacts);
public Collection<Contact> getActiveContacts() throws DbException {
Collection<Contact> contacts =
db.transactionWithResult(true, db::getContacts);
List<Contact> active = new ArrayList<>(contacts.size());
for (Contact c : contacts) if (c.isActive()) active.add(c);
return active;
}
@Override
@@ -162,6 +123,12 @@ class ContactManagerImpl implements ContactManager {
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
public void setContactAlias(Transaction txn, ContactId c,
@Nullable String alias) throws DbException {
@@ -180,14 +147,16 @@ class ContactManagerImpl implements ContactManager {
}
@Override
public boolean contactExists(Transaction txn, AuthorId a)
throws DbException {
return db.containsContact(txn, a);
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
return db.containsContact(txn, remoteAuthorId, localAuthorId);
}
@Override
public boolean contactExists(AuthorId a) throws DbException {
return db.transactionWithResult(true, txn -> contactExists(txn, a));
public boolean contactExists(AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException {
return db.transactionWithResult(true, txn ->
contactExists(txn, remoteAuthorId, localAuthorId));
}
@Override
@@ -209,12 +178,12 @@ class ContactManagerImpl implements ContactManager {
LocalAuthor localAuthor = identityManager.getLocalAuthor(txn);
if (localAuthor.getId().equals(authorId))
return new AuthorInfo(OURSELVES);
if (db.containsContact(txn, authorId)) {
Contact c = db.getContact(txn, authorId);
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
else return new AuthorInfo(UNVERIFIED, c.getAlias());
}
return new AuthorInfo(UNKNOWN);
Collection<Contact> contacts = db.getContactsByAuthorId(txn, authorId);
if (contacts.isEmpty()) return new AuthorInfo(UNKNOWN);
if (contacts.size() > 1) throw new AssertionError();
Contact c = contacts.iterator().next();
if (c.isVerified()) return new AuthorInfo(VERIFIED, c.getAlias());
else return new AuthorInfo(UNVERIFIED, c.getAlias());
}
}

View File

@@ -4,22 +4,18 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.TransportCrypto;
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.OutgoingKeys;
import org.briarproject.bramble.api.transport.TransportKeys;
import org.briarproject.bramble.util.ByteUtils;
import org.briarproject.bramble.util.StringUtils;
import org.spongycastle.crypto.Digest;
import org.spongycastle.crypto.digests.Blake2bDigest;
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_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_TAG_LABEL;
import static org.briarproject.bramble.api.transport.TransportConstants.ROTATE_LABEL;
@@ -28,9 +24,6 @@ import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_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 {
@@ -43,44 +36,45 @@ class TransportCryptoImpl implements TransportCrypto {
@Override
public TransportKeys deriveTransportKeys(TransportId t,
SecretKey rootKey, long timePeriod, boolean weAreAlice,
SecretKey master, long rotationPeriod, boolean alice,
boolean active) {
// Keys for the previous period are derived from the root key
SecretKey inTagPrev = deriveTagKey(rootKey, t, !weAreAlice);
SecretKey inHeaderPrev = deriveHeaderKey(rootKey, t, !weAreAlice);
SecretKey outTagPrev = deriveTagKey(rootKey, t, weAreAlice);
SecretKey outHeaderPrev = deriveHeaderKey(rootKey, t, weAreAlice);
// Keys for the previous period are derived from the master secret
SecretKey inTagPrev = deriveTagKey(master, t, !alice);
SecretKey inHeaderPrev = deriveHeaderKey(master, t, !alice);
SecretKey outTagPrev = deriveTagKey(master, t, alice);
SecretKey outHeaderPrev = deriveHeaderKey(master, t, alice);
// Derive the keys for the current and next periods
SecretKey inTagCurr = rotateKey(inTagPrev, timePeriod);
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, timePeriod);
SecretKey inTagNext = rotateKey(inTagCurr, timePeriod + 1);
SecretKey inHeaderNext = rotateKey(inHeaderCurr, timePeriod + 1);
SecretKey outTagCurr = rotateKey(outTagPrev, timePeriod);
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, timePeriod);
SecretKey inTagCurr = rotateKey(inTagPrev, rotationPeriod);
SecretKey inHeaderCurr = rotateKey(inHeaderPrev, rotationPeriod);
SecretKey inTagNext = rotateKey(inTagCurr, rotationPeriod + 1);
SecretKey inHeaderNext = rotateKey(inHeaderCurr, rotationPeriod + 1);
SecretKey outTagCurr = rotateKey(outTagPrev, rotationPeriod);
SecretKey outHeaderCurr = rotateKey(outHeaderPrev, rotationPeriod);
// Initialise the reordering windows and stream counters
IncomingKeys inPrev = new IncomingKeys(inTagPrev, inHeaderPrev,
timePeriod - 1);
rotationPeriod - 1);
IncomingKeys inCurr = new IncomingKeys(inTagCurr, inHeaderCurr,
timePeriod);
rotationPeriod);
IncomingKeys inNext = new IncomingKeys(inTagNext, inHeaderNext,
timePeriod + 1);
rotationPeriod + 1);
OutgoingKeys outCurr = new OutgoingKeys(outTagCurr, outHeaderCurr,
timePeriod, active);
rotationPeriod, active);
// Collect and return the keys
return new TransportKeys(t, inPrev, inCurr, inNext, outCurr);
}
@Override
public TransportKeys rotateTransportKeys(TransportKeys k, long timePeriod) {
if (k.getTimePeriod() >= timePeriod) return k;
public TransportKeys rotateTransportKeys(TransportKeys k,
long rotationPeriod) {
if (k.getRotationPeriod() >= rotationPeriod) return k;
IncomingKeys inPrev = k.getPreviousIncomingKeys();
IncomingKeys inCurr = k.getCurrentIncomingKeys();
IncomingKeys inNext = k.getNextIncomingKeys();
OutgoingKeys outCurr = k.getCurrentOutgoingKeys();
long startPeriod = outCurr.getTimePeriod();
long startPeriod = outCurr.getRotationPeriod();
boolean active = outCurr.isActive();
// Rotate the keys
for (long p = startPeriod + 1; p <= timePeriod; p++) {
for (long p = startPeriod + 1; p <= rotationPeriod; p++) {
inPrev = inCurr;
inCurr = inNext;
SecretKey inNextTag = rotateKey(inNext.getTagKey(), p + 1);
@@ -95,117 +89,24 @@ class TransportCryptoImpl implements TransportCrypto {
outCurr);
}
private SecretKey rotateKey(SecretKey k, long timePeriod) {
private SecretKey rotateKey(SecretKey k, long rotationPeriod) {
byte[] period = new byte[INT_64_BYTES];
writeUint64(timePeriod, period, 0);
ByteUtils.writeUint64(rotationPeriod, period, 0);
return crypto.deriveKey(ROTATE_LABEL, k, period);
}
private SecretKey deriveTagKey(SecretKey rootKey, TransportId t,
boolean keyBelongsToAlice) {
String label = keyBelongsToAlice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
byte[] id = toUtf8(t.getString());
return crypto.deriveKey(label, rootKey, id);
private SecretKey deriveTagKey(SecretKey master, TransportId t,
boolean alice) {
String label = alice ? ALICE_TAG_LABEL : BOB_TAG_LABEL;
byte[] id = StringUtils.toUtf8(t.getString());
return crypto.deriveKey(label, master, id);
}
private SecretKey deriveHeaderKey(SecretKey rootKey, TransportId t,
boolean keyBelongsToAlice) {
String label = keyBelongsToAlice ? ALICE_HEADER_LABEL :
BOB_HEADER_LABEL;
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);
}
private SecretKey deriveHeaderKey(SecretKey master, TransportId t,
boolean alice) {
String label = alice ? ALICE_HEADER_LABEL : BOB_HEADER_LABEL;
byte[] id = StringUtils.toUtf8(t.getString());
return crypto.deriveKey(label, master, id);
}
@Override
@@ -224,14 +125,14 @@ class TransportCryptoImpl implements TransportCrypto {
// The input is the protocol version as a 16-bit integer, followed by
// the stream number as a 64-bit integer
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
writeUint16(protocolVersion, protocolVersionBytes, 0);
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES];
writeUint64(streamNumber, streamNumberBytes, 0);
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
byte[] mac = new byte[macLength];
prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC
arraycopy(mac, 0, tag, 0, TAG_LENGTH);
System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
}
}

View File

@@ -2,13 +2,9 @@ package org.briarproject.bramble.db;
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.contact.PendingContactState;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DataTooNewException;
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.MessageDeletedException;
import org.briarproject.bramble.api.db.Metadata;
@@ -27,11 +23,8 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.sync.validation.MessageState;
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.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.Collection;
@@ -40,14 +33,11 @@ import java.util.Map;
import javax.annotation.Nullable;
/**
* A low-level interface to the database ({@link DatabaseComponent} provides a
* high-level interface).
* <p/>
* Most operations take a transaction argument, which is obtained by calling
* {@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.
* A low-level interface to the database (DatabaseComponent provides a
* high-level interface). Most operations take a transaction argument, which is
* obtained by calling {@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
interface Database<T> {
@@ -87,10 +77,11 @@ interface Database<T> {
void commitTransaction(T txn) throws DbException;
/**
* Stores a contact with the given pseudonym and returns an ID for the
* contact.
* Stores a contact associated with the given local and remote pseudonyms,
* and returns an ID for the contact.
*/
ContactId addContact(T txn, Author a, boolean verified) throws DbException;
ContactId addContact(T txn, Author remote, AuthorId local, boolean verified,
boolean active) throws DbException;
/**
* Stores a group.
@@ -104,20 +95,6 @@ interface Database<T> {
void addGroupVisibility(T txn, ContactId c, GroupId g, boolean shared)
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.
*/
@@ -144,11 +121,6 @@ interface Database<T> {
*/
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.
*/
@@ -159,15 +131,17 @@ interface Database<T> {
* Stores the given transport keys for the given contact and returns a
* key set ID.
*/
TransportKeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
KeySetId addTransportKeys(T txn, ContactId c, TransportKeys k)
throws DbException;
/**
* Returns true if the database contains the given contact.
* Returns true if the database contains the given contact for the given
* local pseudonym.
* <p/>
* Read-only.
*/
boolean containsContact(T txn, AuthorId a) throws DbException;
boolean containsContact(T txn, AuthorId remote, AuthorId local)
throws DbException;
/**
* Returns true if the database contains the given contact.
@@ -197,14 +171,6 @@ interface Database<T> {
*/
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.
* <p/>
@@ -249,13 +215,6 @@ interface Database<T> {
*/
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.
* <p/>
@@ -263,6 +222,28 @@ interface Database<T> {
*/
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.
* <p/>
@@ -303,14 +284,6 @@ interface Database<T> {
Map<ContactId, Boolean> getGroupVisibility(T txn, GroupId g)
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.
* <p/>
@@ -501,13 +474,6 @@ interface Database<T> {
*/
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
* given contact and have been requested by the contact, up to the given
@@ -530,19 +496,13 @@ interface Database<T> {
* <p/>
* Read-only.
*/
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)
Collection<KeySet> getTransportKeys(T txn, TransportId t)
throws DbException;
/**
* Increments the outgoing stream counter for the given transport keys.
*/
void incrementStreamCounter(T txn, TransportId t, TransportKeySetId k)
void incrementStreamCounter(T txn, TransportId t, KeySetId k)
throws DbException;
/**
@@ -611,12 +571,6 @@ interface Database<T> {
void removeGroupVisibility(T txn, ContactId c, GroupId g)
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.
*/
@@ -634,11 +588,6 @@ interface Database<T> {
void removeOfferedMessages(T txn, ContactId c,
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.
*/
@@ -647,7 +596,7 @@ interface Database<T> {
/**
* Removes the given transport keys from the database.
*/
void removeTransportKeys(T txn, TransportId t, TransportKeySetId k)
void removeTransportKeys(T txn, TransportId t, KeySetId k)
throws DbException;
/**
@@ -661,6 +610,12 @@ interface Database<T> {
*/
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.
*/
@@ -686,29 +641,16 @@ interface Database<T> {
throws DbException;
/**
* Sets the state of the given pending contact.
* Sets the reordering window for the given key set and transport in the
* given rotation period.
*/
void setPendingContactState(T txn, PendingContactId p,
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;
void setReorderingWindow(T txn, KeySetId k, TransportId t,
long rotationPeriod, long base, byte[] bitmap) throws DbException;
/**
* Marks the given transport keys as usable for outgoing streams.
*/
void setTransportKeysActive(T txn, TransportId t, TransportKeySetId k)
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
throws DbException;
/**
@@ -719,13 +661,8 @@ interface Database<T> {
void updateExpiryTimeAndEta(T txn, ContactId c, MessageId m, int maxLatency)
throws DbException;
/**
* Updates the given handshake keys.
*/
void updateHandshakeKeys(T txn, HandshakeKeySet ks) throws DbException;
/**
* Updates the given transport keys following key rotation.
*/
void updateTransportKeys(T txn, TransportKeySet ks) throws DbException;
void updateTransportKeys(T txn, KeySet ks) throws DbException;
}

View File

@@ -2,34 +2,27 @@ package org.briarproject.bramble.db;
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.contact.event.ContactAddedEvent;
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.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.DatabaseComponent;
import org.briarproject.bramble.api.db.DbCallable;
import org.briarproject.bramble.api.db.DbException;
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.MigrationListener;
import org.briarproject.bramble.api.db.NoSuchContactException;
import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchLocalAuthorException;
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.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.event.Event;
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.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
@@ -62,11 +55,8 @@ import org.briarproject.bramble.api.sync.event.MessageToRequestEvent;
import org.briarproject.bramble.api.sync.event.MessagesAckedEvent;
import org.briarproject.bramble.api.sync.event.MessagesSentEvent;
import org.briarproject.bramble.api.sync.validation.MessageState;
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.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeySetId;
import org.briarproject.bramble.api.transport.KeySet;
import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeys;
import java.util.ArrayList;
@@ -74,7 +64,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.logging.Logger;
@@ -103,29 +92,25 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
private final Database<T> db;
private final Class<T> txnClass;
private final EventBus eventBus;
private final Executor eventExecutor;
private final ShutdownManager shutdownManager;
private final ShutdownManager shutdown;
private final AtomicBoolean closed = new AtomicBoolean(false);
private final ReentrantReadWriteLock lock =
new ReentrantReadWriteLock(true);
private final Visitor visitor = new CommitActionVisitor();
@Inject
DatabaseComponentImpl(Database<T> db, Class<T> txnClass, EventBus eventBus,
@EventExecutor Executor eventExecutor,
ShutdownManager shutdownManager) {
ShutdownManager shutdown) {
this.db = db;
this.txnClass = txnClass;
this.eventBus = eventBus;
this.eventExecutor = eventExecutor;
this.shutdownManager = shutdownManager;
this.shutdown = shutdown;
}
@Override
public boolean open(SecretKey key, @Nullable MigrationListener listener)
throws DbException {
boolean reopened = db.open(key, listener);
shutdownManager.addShutdownHook(() -> {
shutdown.addShutdownHook(() -> {
try {
close();
} catch (DbException e) {
@@ -175,16 +160,13 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
public void endTransaction(Transaction transaction) {
try {
T txn = txnClass.cast(transaction.unbox());
if (transaction.isCommitted()) {
for (CommitAction a : transaction.getActions())
a.accept(visitor);
} else {
db.abortTransaction(txn);
}
if (!transaction.isCommitted()) db.abortTransaction(txn);
} finally {
if (transaction.isReadOnly()) lock.readLock().unlock();
else lock.writeLock().unlock();
}
if (transaction.isCommitted())
for (Event e : transaction.getEvents()) eventBus.broadcast(e);
}
@Override
@@ -232,16 +214,20 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public ContactId addContact(Transaction transaction, Author a,
boolean verified) throws DbException {
public ContactId addContact(Transaction transaction, Author remote,
AuthorId local, boolean verified, boolean active)
throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (db.containsLocalAuthor(txn, a.getId()))
if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException();
if (db.containsLocalAuthor(txn, remote.getId()))
throw new ContactExistsException();
if (db.containsContact(txn, a.getId()))
if (db.containsContact(txn, remote.getId(), local))
throw new ContactExistsException();
ContactId c = db.addContact(txn, a, verified);
transaction.attach(new ContactAddedEvent(c));
ContactId c = db.addContact(txn, remote, local, verified, active);
transaction.attach(new ContactAddedEvent(c, active));
if (active) transaction.attach(new ContactStatusChangedEvent(c, true));
return c;
}
@@ -255,30 +241,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
}
@Override
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
ContactId c, HandshakeKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addHandshakeKeys(txn, c, k);
}
@Override
public HandshakeKeySetId addHandshakeKeys(Transaction transaction,
PendingContactId p, HandshakeKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsPendingContact(txn, p))
throw new NoSuchPendingContactException();
if (!db.containsTransport(txn, k.getTransportId()))
throw new NoSuchTransportException();
return db.addHandshakeKeys(txn, p, k);
}
@Override
public void addLocalAuthor(Transaction transaction, LocalAuthor a)
throws DbException {
@@ -307,16 +269,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public void addTransport(Transaction transaction, TransportId t,
int maxLatency) throws DbException {
@@ -327,8 +279,8 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public TransportKeySetId addTransportKeys(Transaction transaction,
ContactId c, TransportKeys k) throws DbException {
public KeySetId addTransportKeys(Transaction transaction, ContactId c,
TransportKeys k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
@@ -339,10 +291,12 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public boolean containsContact(Transaction transaction, AuthorId a)
throws DbException {
public boolean containsContact(Transaction transaction, AuthorId remote,
AuthorId local) throws DbException {
T txn = unbox(transaction);
return db.containsContact(txn, a);
if (!db.containsLocalAuthor(txn, local))
throw new NoSuchLocalAuthorException();
return db.containsContact(txn, remote, local);
}
@Override
@@ -359,13 +313,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public void deleteMessage(Transaction transaction, MessageId m)
throws DbException {
@@ -482,15 +429,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public Collection<Contact> getContacts(Transaction transaction)
throws DbException {
@@ -498,6 +436,22 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public Group getGroup(Transaction transaction, GroupId g)
throws DbException {
@@ -532,15 +486,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public LocalAuthor getLocalAuthor(Transaction transaction, AuthorId a)
throws DbException {
@@ -698,13 +643,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
return db.getNextSendTime(txn, c);
}
@Override
public Collection<PendingContact> getPendingContacts(
Transaction transaction) throws DbException {
T txn = unbox(transaction);
return db.getPendingContacts(txn);
}
@Override
public Settings getSettings(Transaction transaction, String namespace)
throws DbException {
@@ -713,7 +651,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public Collection<TransportKeySet> getTransportKeys(Transaction transaction,
public Collection<KeySet> getTransportKeys(Transaction transaction,
TransportId t) throws DbException {
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
@@ -723,17 +661,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
public void incrementStreamCounter(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.incrementStreamCounter(txn, t, k);
}
@Override
public void incrementStreamCounter(Transaction transaction, TransportId t,
TransportKeySetId k) throws DbException {
KeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
@@ -882,16 +810,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public void removeLocalAuthor(Transaction transaction, AuthorId a)
throws DbException {
@@ -914,16 +832,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public void removeTransport(Transaction transaction, TransportId t)
throws DbException {
@@ -936,7 +844,7 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
@Override
public void removeTransportKeys(Transaction transaction,
TransportId t, TransportKeySetId k) throws DbException {
TransportId t, KeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
@@ -955,6 +863,17 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
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
public void setContactAlias(Transaction transaction, ContactId c,
@Nullable String alias) throws DbException {
@@ -1024,30 +943,19 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
}
@Override
public void setReorderingWindow(Transaction transaction,
TransportKeySetId k, TransportId t, long timePeriod, long base,
byte[] bitmap) throws DbException {
public void setReorderingWindow(Transaction transaction, KeySetId k,
TransportId t, long rotationPeriod, 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
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);
db.setReorderingWindow(txn, k, t, rotationPeriod, base, bitmap);
}
@Override
public void setTransportKeysActive(Transaction transaction, TransportId t,
TransportKeySetId k) throws DbException {
KeySetId k) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsTransport(txn, t))
@@ -1055,40 +963,15 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
db.setTransportKeysActive(txn, t, k);
}
@Override
public void updateHandshakeKeys(Transaction transaction,
Collection<HandshakeKeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
for (HandshakeKeySet ks : keys) {
TransportId t = ks.getKeys().getTransportId();
if (db.containsTransport(txn, t))
db.updateHandshakeKeys(txn, ks);
}
}
@Override
public void updateTransportKeys(Transaction transaction,
Collection<TransportKeySet> keys) throws DbException {
Collection<KeySet> keys) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
for (TransportKeySet ks : keys) {
TransportId t = ks.getKeys().getTransportId();
for (KeySet ks : keys) {
TransportId t = ks.getTransportKeys().getTransportId();
if (db.containsTransport(txn, t))
db.updateTransportKeys(txn, ks);
}
}
private class CommitActionVisitor implements Visitor {
@Override
public void visit(EventAction a) {
eventBus.broadcast(a.getEvent());
}
@Override
public void visit(TaskAction a) {
eventExecutor.execute(a.getTask());
}
}
}

View File

@@ -3,13 +3,11 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseConfig;
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.sync.MessageFactory;
import org.briarproject.bramble.api.system.Clock;
import java.sql.Connection;
import java.util.concurrent.Executor;
import javax.inject.Singleton;
@@ -29,9 +27,8 @@ public class DatabaseModule {
@Provides
@Singleton
DatabaseComponent provideDatabaseComponent(Database<Connection> db,
EventBus eventBus, @EventExecutor Executor eventExecutor,
ShutdownManager shutdownManager) {
EventBus eventBus, ShutdownManager shutdown) {
return new DatabaseComponentImpl<>(db, Connection.class, eventBus,
eventExecutor, shutdownManager);
shutdown);
}
}

View File

@@ -15,23 +15,16 @@ import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
/**
* Contains all the H2-specific code for the database.
*/
@NotNullByDefault
class H2Database extends JdbcDatabase {
private static final Logger LOG = getLogger(H2Database.class.getName());
private static final String HASH_TYPE = "BINARY(32)";
private static final String SECRET_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY";
@@ -76,6 +69,30 @@ class H2Database extends JdbcDatabase {
}
}
@Override
public long getFreeSpace() {
File dir = config.getDatabaseDirectory();
long maxSize = config.getMaxSize();
long free = dir.getFreeSpace();
long used = getDiskSpace(dir);
long quota = maxSize - used;
return Math.min(free, quota);
}
private long getDiskSpace(File f) {
if (f.isDirectory()) {
long total = 0;
File[] children = f.listFiles();
if (children != null)
for (File child : children) total += getDiskSpace(child);
return total;
} else if (f.isFile()) {
return f.length();
} else {
return 0;
}
}
@Override
protected Connection createConnection() throws SQLException {
SecretKey key = this.key;
@@ -104,8 +121,8 @@ class H2Database extends JdbcDatabase {
s.close();
c.close();
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
tryToClose(c, LOG, WARNING);
tryToClose(s);
tryToClose(c);
throw new DbException(e);
}
}

View File

@@ -14,24 +14,16 @@ import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
/**
* Contains all the HSQLDB-specific code for the database.
*/
@NotNullByDefault
class HyperSqlDatabase extends JdbcDatabase {
private static final Logger LOG =
getLogger(HyperSqlDatabase.class.getName());
private static final String HASH_TYPE = "BINARY(32)";
private static final String SECRET_TYPE = "BINARY(32)";
private static final String BINARY_TYPE = "BINARY";
@@ -80,12 +72,36 @@ class HyperSqlDatabase extends JdbcDatabase {
s.close();
c.close();
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
tryToClose(c, LOG, WARNING);
tryToClose(s);
tryToClose(c);
throw new DbException(e);
}
}
@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
protected Connection createConnection() throws SQLException {
SecretKey key = this.key;
@@ -106,8 +122,8 @@ class HyperSqlDatabase extends JdbcDatabase {
s.close();
c.close();
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
tryToClose(c, LOG, WARNING);
tryToClose(s);
tryToClose(c);
throw new DbException(e);
}
}

View File

@@ -1,42 +0,0 @@
package org.briarproject.bramble.db;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class JdbcUtils {
static void tryToClose(@Nullable ResultSet rs, Logger logger, Level level) {
try {
if (rs != null) rs.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
static void tryToClose(@Nullable Statement s, Logger logger, Level level) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
static void tryToClose(@Nullable Connection c, Logger logger, Level level) {
try {
if (c != null) c.close();
} catch (SQLException e) {
logException(logger, level, e);
}
}
}

View File

@@ -7,8 +7,10 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
class Migration38_39 implements Migration<Connection> {
@@ -38,8 +40,16 @@ class Migration38_39 implements Migration<Connection> {
+ " ALTER COLUMN contactId"
+ " SET NOT NULL");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
tryToClose(s);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -7,8 +7,10 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
class Migration39_40 implements Migration<Connection> {
@@ -37,8 +39,16 @@ class Migration39_40 implements Migration<Connection> {
+ " ALTER COLUMN eta"
+ " SET NOT NULL");
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
tryToClose(s);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -7,9 +7,11 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.db.JdbcUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
class Migration40_41 implements Migration<Connection> {
@@ -17,7 +19,7 @@ class Migration40_41 implements Migration<Connection> {
private final DatabaseTypes dbTypes;
Migration40_41(DatabaseTypes databaseTypes) {
public Migration40_41(DatabaseTypes databaseTypes) {
this.dbTypes = databaseTypes;
}
@@ -39,8 +41,16 @@ class Migration40_41 implements Migration<Connection> {
s.execute("ALTER TABLE contacts"
+ dbTypes.replaceTypes(" ADD alias _STRING"));
} catch (SQLException e) {
tryToClose(s, LOG, WARNING);
tryToClose(s);
throw new DbException(e);
}
}
private void tryToClose(@Nullable Statement s) {
try {
if (s != null) s.close();
} catch (SQLException e) {
logException(LOG, WARNING, e);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -4,11 +4,12 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class FileTransportReader implements TransportConnectionReader {
@@ -33,7 +34,11 @@ class FileTransportReader implements TransportConnectionReader {
@Override
public void dispose(boolean exception, boolean recognised) {
tryToClose(in, LOG, WARNING);
try {
in.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
plugin.readerFinished(file, exception, recognised);
}
}

View File

@@ -4,11 +4,12 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
class FileTransportWriter implements TransportConnectionWriter {
@@ -43,7 +44,11 @@ class FileTransportWriter implements TransportConnectionWriter {
@Override
public void dispose(boolean exception) {
tryToClose(out, LOG, WARNING);
try {
out.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
plugin.writerFinished(file, exception);
}
}

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
@@ -36,6 +35,7 @@ import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PREF_LAN_IP_PORTS;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.PROP_IP_PORTS;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.PrivacyUtils.scrubSocketAddress;
@NotNullByDefault
@@ -293,7 +293,11 @@ class LanTcpPlugin extends TcpPlugin {
@Override
public void close() {
IoUtils.tryToClose(ss, LOG, WARNING);
try {
ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -11,7 +11,6 @@ import org.briarproject.bramble.api.plugin.duplex.DuplexPlugin;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginCallback;
import org.briarproject.bramble.api.plugin.duplex.DuplexTransportConnection;
import org.briarproject.bramble.api.properties.TransportProperties;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.IOException;
@@ -154,8 +153,13 @@ abstract class TcpPlugin implements DuplexPlugin {
}
protected void tryToClose(@Nullable ServerSocket ss) {
IoUtils.tryToClose(ss, LOG, WARNING);
callback.transportDisabled();
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
} finally {
callback.transportDisabled();
}
}
String getIpPortString(InetSocketAddress a) {

View File

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

View File

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

View File

@@ -31,6 +31,7 @@ import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.util.IoUtils;
import java.io.Closeable;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
@@ -69,7 +70,6 @@ import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_AUTOMATIC;
import static org.briarproject.bramble.api.plugin.TorConstants.PREF_TOR_NETWORK_NEVER;
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.PROP_ONION_V2;
import static org.briarproject.bramble.api.plugin.TorConstants.PROP_ONION_V3;
@@ -303,8 +303,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
IoUtils.copyAndClose(in, out);
doneFile.createNewFile();
} catch (IOException e) {
IoUtils.tryToClose(in, LOG, WARNING);
IoUtils.tryToClose(out, LOG, WARNING);
tryToClose(in);
tryToClose(out);
throw new PluginException(e);
}
}
@@ -341,6 +341,22 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
return getClass().getClassLoader().getResourceAsStream("torrc");
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void tryToClose(@Nullable Socket s) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void listFiles(File f) {
if (f.isDirectory()) {
File[] children = f.listFiles();
@@ -362,7 +378,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
return b;
} finally {
IoUtils.tryToClose(in, LOG, WARNING);
tryToClose(in);
}
}
@@ -402,8 +418,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
}
private void tryToClose(@Nullable ServerSocket ss) {
IoUtils.tryToClose(ss, LOG, WARNING);
callback.transportDisabled();
try {
if (ss != null) ss.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
} finally {
callback.transportDisabled();
}
}
private void publishHiddenService(String port) {
@@ -470,19 +491,13 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!enable) callback.transportDisabled();
}
private void enableBridges(boolean enable, boolean needsMeek)
throws IOException {
private void enableBridges(boolean enable) throws IOException {
if (enable) {
Collection<String> conf = new ArrayList<>();
conf.add("UseBridges 1");
if (needsMeek) {
conf.add("ClientTransportPlugin meek_lite exec " +
obfs4File.getAbsolutePath());
} else {
conf.add("ClientTransportPlugin obfs4 exec " +
obfs4File.getAbsolutePath());
}
conf.addAll(circumventionProvider.getBridges(needsMeek));
conf.add("ClientTransportPlugin obfs4 exec " +
obfs4File.getAbsolutePath());
conf.addAll(circumventionProvider.getBridges());
controlConnection.setConf(conf);
} else {
controlConnection.setConf("UseBridges", "0");
@@ -578,7 +593,7 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
LOG.info("Could not connect to " + scrubOnion(bestOnion)
+ ": " + e.toString());
}
IoUtils.tryToClose(s, LOG, WARNING);
tryToClose(s);
return null;
}
}
@@ -655,10 +670,8 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (s.getNamespace().equals(ID.getString())) {
LOG.info("Tor settings updated");
settings = s.getSettings();
// Works around a bug introduced in Tor 0.3.4.8.
// https://trac.torproject.org/projects/tor/ticket/28027
// Could be replaced with callback.transportDisabled()
// when fixed.
// Works around a bug introduced in Tor 0.3.4.8. Could be
// replaced with callback.transportDisabled() when fixed.
disableNetwork();
updateConnectionStatus(networkManager.getNetworkStatus(),
batteryManager.isCharging());
@@ -694,8 +707,6 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
int network = settings.getInt(PREF_TOR_NETWORK,
PREF_TOR_NETWORK_AUTOMATIC);
boolean useMobile = settings.getBoolean(PREF_TOR_MOBILE, true);
boolean onlyWhenCharging =
settings.getBoolean(PREF_TOR_ONLY_WHEN_CHARGING, false);
boolean bridgesWork = circumventionProvider.doBridgesWork(country);
boolean automatic = network == PREF_TOR_NETWORK_AUTOMATIC;
@@ -710,29 +721,21 @@ abstract class TorPlugin implements DuplexPlugin, EventHandler, EventListener {
if (!online) {
LOG.info("Disabling network, device is offline");
enableNetwork(false);
} else if (!charging && onlyWhenCharging) {
LOG.info("Disabling network, device is on battery");
enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_NEVER ||
(!useMobile && !wifi)) {
LOG.info("Disabling network, device is using mobile data");
LOG.info("Disabling network due to setting");
enableNetwork(false);
} else if (automatic && blocked && !bridgesWork) {
LOG.info("Disabling network, country is blocked");
enableNetwork(false);
} else if (network == PREF_TOR_NETWORK_WITH_BRIDGES ||
(automatic && bridgesWork)) {
if (circumventionProvider.needsMeek(country)) {
LOG.info("Enabling network, using meek bridges");
enableBridges(true, true);
} else {
LOG.info("Enabling network, using obfs4 bridges");
enableBridges(true, false);
}
LOG.info("Enabling network, using bridges");
enableBridges(true);
enableNetwork(true);
} else {
LOG.info("Enabling network, not using bridges");
enableBridges(false, false);
enableBridges(false);
enableNetwork(true);
}
if (online && wifi && charging) {

View File

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

View File

@@ -12,6 +12,7 @@ import org.briarproject.bramble.api.reporting.DevReporter;
import org.briarproject.bramble.util.IoUtils;
import org.briarproject.bramble.util.StringUtils;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
@@ -25,12 +26,13 @@ import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import javax.net.SocketFactory;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.IoUtils.tryToClose;
import static org.briarproject.bramble.util.LogUtils.logException;
@Immutable
@NotNullByDefault
@@ -64,7 +66,7 @@ class DevReporterImpl implements DevReporter, EventListener {
s.setSoTimeout(SOCKET_TIMEOUT);
return s;
} catch (IOException e) {
tryToClose(s, LOG, WARNING);
tryToClose(s);
throw e;
}
}
@@ -86,7 +88,8 @@ class DevReporterImpl implements DevReporter, EventListener {
writer.append(armoured);
writer.flush();
} finally {
tryToClose(writer, LOG, WARNING);
if (writer != null)
writer.close();
}
}
@@ -118,11 +121,27 @@ class DevReporterImpl implements DevReporter, EventListener {
f.delete();
} catch (IOException e) {
LOG.log(WARNING, "Failed to send reports", e);
tryToClose(out, LOG, WARNING);
tryToClose(in, LOG, WARNING);
tryToClose(out);
tryToClose(in);
return;
}
}
LOG.info("Reports sent");
}
private void tryToClose(@Nullable Closeable c) {
try {
if (c != null) c.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
private void tryToClose(@Nullable Socket s) {
try {
if (s != null) s.close();
} catch (IOException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -13,33 +13,32 @@ import java.util.logging.Logger;
import javax.annotation.concurrent.Immutable;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
@Immutable
@NotNullByDefault
class UnixSecureRandomProvider extends AbstractSecureRandomProvider {
class LinuxSecureRandomProvider extends AbstractSecureRandomProvider {
private static final Logger LOG =
getLogger(UnixSecureRandomProvider.class.getName());
Logger.getLogger(LinuxSecureRandomProvider.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final AtomicBoolean seeded = new AtomicBoolean(false);
private final File outputDevice;
UnixSecureRandomProvider() {
LinuxSecureRandomProvider() {
this(RANDOM_DEVICE);
}
UnixSecureRandomProvider(File outputDevice) {
LinuxSecureRandomProvider(File outputDevice) {
this.outputDevice = outputDevice;
}
@Override
public Provider getProvider() {
if (!seeded.getAndSet(true)) writeSeed();
return new UnixProvider();
return new LinuxProvider();
}
protected void writeSeed() {
@@ -56,15 +55,15 @@ class UnixSecureRandomProvider extends AbstractSecureRandomProvider {
}
// Based on https://android-developers.googleblog.com/2013/08/some-securerandom-thoughts.html
private static class UnixProvider extends Provider {
private static class LinuxProvider extends Provider {
private UnixProvider() {
super("UnixPRNG", 1.0, "A Unix-specific PRNG using /dev/urandom");
private LinuxProvider() {
super("LinuxPRNG", 1.1, "A Linux-specific PRNG using /dev/urandom");
// Although /dev/urandom is not a SHA-1 PRNG, some callers
// explicitly request a SHA1PRNG SecureRandom and we need to
// prevent them from getting the default implementation whose
// output may have low entropy.
put("SecureRandom.SHA1PRNG", UnixSecureRandomSpi.class.getName());
put("SecureRandom.SHA1PRNG", LinuxSecureRandomSpi.class.getName());
put("SecureRandom.SHA1PRNG ImplementedIn", "Software");
}
}

View File

@@ -10,24 +10,22 @@ import java.security.SecureRandomSpi;
import java.util.logging.Logger;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException;
public class UnixSecureRandomSpi extends SecureRandomSpi {
public class LinuxSecureRandomSpi extends SecureRandomSpi {
private static final Logger LOG =
getLogger(UnixSecureRandomSpi.class.getName());
Logger.getLogger(LinuxSecureRandomSpi.class.getName());
private static final File RANDOM_DEVICE = new File("/dev/urandom");
private final File inputDevice, outputDevice;
@SuppressWarnings("WeakerAccess")
public UnixSecureRandomSpi() {
public LinuxSecureRandomSpi() {
this(RANDOM_DEVICE, RANDOM_DEVICE);
}
UnixSecureRandomSpi(File inputDevice, File outputDevice) {
LinuxSecureRandomSpi(File inputDevice, File outputDevice) {
this.inputDevice = inputDevice;
this.outputDevice = outputDevice;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,5 +1,4 @@
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 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 meek_lite 0.0.2.0:2 97700DFE9F483596DDA6264C4D7DF7641E1E39CE url=https://meek.azureedge.net/ front=ajax.aspnetcdn.com
Bridge obfs4 45.33.37.112:9443 60A609BB4ABE8D46E634AE81ED29ADAB7776B399 cert=t5v19WmNv5Sc2YPNr8RQids365W7MY8zJwQVkOxBjUMFomMWARDzsbYpcWLLcw0J9Gm+BQ iat-mode=0

View File

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

View File

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

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