diff --git a/.idea/runConfigurations/All_tests.xml b/.idea/runConfigurations/All_tests.xml
index 1882b817b..71857d973 100644
--- a/.idea/runConfigurations/All_tests.xml
+++ b/.idea/runConfigurations/All_tests.xml
@@ -21,6 +21,7 @@
+
diff --git a/bramble-android/build.gradle b/bramble-android/build.gradle
index b41f1cebe..f7c9415ed 100644
--- a/bramble-android/build.gradle
+++ b/bramble-android/build.gradle
@@ -34,6 +34,14 @@ dependencies {
compileOnly 'javax.annotation:jsr250-api:1.0'
+ testImplementation project(path: ':bramble-api', configuration: 'testOutput')
+ testImplementation 'junit:junit:4.12'
+ testImplementation "org.jmock:jmock:2.8.2"
+ testImplementation "org.jmock:jmock-junit4:2.8.2"
+ testImplementation "org.jmock:jmock-legacy:2.8.2"
+ testImplementation "org.hamcrest:hamcrest-library:1.3"
+ testImplementation "org.hamcrest:hamcrest-core:1.3"
+
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')
androidTestImplementation project(path: ':bramble-core', configuration: 'testOutput')
androidTestImplementation 'com.android.support.test:runner:1.0.2'
@@ -43,6 +51,10 @@ dependencies {
dependencyVerification {
verify = [
+ 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
+ 'com.android.support.test:monitor:1.0.2:monitor-1.0.2.aar:38ef4fa98a32dc55550ff49bb36a583e178b3a9b830fcb8dcc27bfc4254bc2bc',
+ 'com.android.support.test:runner:1.0.2:runner-1.0.2.aar:f04b9ae342975ba1cb3e4a06e13426e3e6b8a73faa45acba604493d83c9a4f00',
+ 'com.android.support:support-annotations:27.1.1:support-annotations-27.1.1.jar:3365960206c3d2b09e845f555e7f88f8effc8d2f00b369e66c4be384029299cf',
'com.android.tools.analytics-library:protos:26.1.3:protos-26.1.3.jar:818c9f256f141d9dafec03a1aa2b94d240b2c140acfd7ee31a8b3e6c2b9479e3',
'com.android.tools.analytics-library:shared:26.1.3:shared-26.1.3.jar:7110706c7ada96c8b6f5ca80c478291bc7899d46277de2c48527e045442401a3',
'com.android.tools.analytics-library:tracker:26.1.3:tracker-26.1.3.jar:4155424bf2ce4872da83332579a1707252bc66cbd77c5144fdc4483d0f2e1418',
@@ -91,12 +103,16 @@ dependencyVerification {
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'javax.xml.bind:jaxb-api:2.2.12-b140109.1041:jaxb-api-2.2.12-b140109.1041.jar:b5e60cd8b7b5ff01ce4a74c5dd008f4fbd14ced3495d0b47b85cfedc182211f2',
+ 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
+ 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
+ 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'org.apache.httpcomponents:httpclient:4.2.6:httpclient-4.2.6.jar:362e9324ee7c697e21279e20077b52737ddef3f1b2c1a7abe5ad34b465145550',
'org.apache.httpcomponents:httpcore:4.2.5:httpcore-4.2.5.jar:e5e82da4cc66c8d917bbf743e3c0752efe8522735e7fc9dbddb65bccea81cfe9',
'org.apache.httpcomponents:httpmime:4.1:httpmime-4.1.jar:31629566148e8a47688ae43b420abc3ecd783ed15b33bebc00824bf24c9b15aa',
+ '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:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
@@ -106,17 +122,25 @@ dependencyVerification {
'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.2.0:kotlin-reflect-1.2.0.jar:4f48a872bad6e4d9c053f4ad610d11e4012ad7e58dc19a03dd5eb811f36069dd',
'org.jetbrains.kotlin:kotlin-stdlib-jre7:1.2.0:kotlin-stdlib-jre7-1.2.0.jar:c7a20fb951d437797afe8980aff6c1e5a03f310c661ba58ba1d4fa90cb0f2926',
'org.jetbrains.kotlin:kotlin-stdlib-jre8:1.2.0:kotlin-stdlib-jre8-1.2.0.jar:633524eee6ef1941f7cb1dab7ee3927b0a221ceee9047aeb5515f4cbb990c82a',
'org.jetbrains.kotlin:kotlin-stdlib:1.2.0:kotlin-stdlib-1.2.0.jar:05cfd9f5ac0b41910703a8925f7211a495909b27a2ffdd1c5106f1689aeafcd4',
'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',
+ 'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
+ 'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
+ 'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.jvnet.staxex:stax-ex:1.7.7:stax-ex-1.7.7.jar:a31ff7d77163c0deb09e7fee59ad35ae44c2cee2cc8552a116ccd1583d813fb4',
+ 'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm-analysis:5.1:asm-analysis-5.1.jar:a34658f5c5de4b573eef21131cc32cc25f7b66407944f312b28ec2e56abb1fa9',
'org.ow2.asm:asm-commons:5.1:asm-commons-5.1.jar:97b3786e1f55e74bddf8ad102bf50e33bbcbc1f6b7fd7b36f0bbbb25cd4981be',
'org.ow2.asm:asm-tree:5.1:asm-tree-5.1.jar:c0de2bbc4cb8297419659813ecd4ed1d077ed1dd5c1f5544cc5143e493e84c10',
'org.ow2.asm:asm-util:5.1:asm-util-5.1.jar:ee032c39ae5e3cd099148fbba9a2124f9ed613e5cb93e03ee0fa8808ce364040',
+ 'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.ow2.asm:asm:5.1:asm-5.1.jar:d2da399a9967c69f0a21739256fa79d284222c223082cacadc17372244764b54',
]
}
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/account/AndroidAccountManager.java b/bramble-android/src/main/java/org/briarproject/bramble/account/AndroidAccountManager.java
new file mode 100644
index 000000000..f23e249ae
--- /dev/null
+++ b/bramble-android/src/main/java/org/briarproject/bramble/account/AndroidAccountManager.java
@@ -0,0 +1,108 @@
+package org.briarproject.bramble.account;
+
+import android.app.Application;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+
+import org.briarproject.bramble.api.account.AccountManager;
+import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.util.IoUtils;
+
+import java.io.File;
+import java.util.logging.Logger;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+class AndroidAccountManager extends AccountManagerImpl
+ implements AccountManager {
+
+ private static final Logger LOG =
+ Logger.getLogger(AndroidAccountManager.class.getName());
+
+ private static final String PREF_DB_KEY = "key";
+
+ protected final Context appContext;
+ private final SharedPreferences prefs;
+
+ @Inject
+ AndroidAccountManager(DatabaseConfig databaseConfig,
+ CryptoComponent crypto, IdentityManager identityManager,
+ SharedPreferences prefs, Application app) {
+ super(databaseConfig, crypto, identityManager);
+ this.prefs = prefs;
+ appContext = app.getApplicationContext();
+ }
+
+ // Locking: stateChangeLock
+ @Override
+ @Nullable
+ protected String loadEncryptedDatabaseKey() {
+ String key = getDatabaseKeyFromPreferences();
+ if (key == null) key = super.loadEncryptedDatabaseKey();
+ else migrateDatabaseKeyToFile(key);
+ return key;
+ }
+
+ // Locking: stateChangeLock
+ @Nullable
+ private String getDatabaseKeyFromPreferences() {
+ String key = prefs.getString(PREF_DB_KEY, null);
+ if (key == null) LOG.info("No database key in preferences");
+ else LOG.info("Found database key in preferences");
+ return key;
+ }
+
+ // Locking: stateChangeLock
+ private void migrateDatabaseKeyToFile(String key) {
+ if (storeEncryptedDatabaseKey(key)) {
+ if (prefs.edit().remove(PREF_DB_KEY).commit())
+ LOG.info("Database key migrated to file");
+ else LOG.warning("Database key not removed from preferences");
+ } else {
+ LOG.warning("Database key not migrated to file");
+ }
+ }
+
+ @Override
+ public void deleteAccount() {
+ synchronized (stateChangeLock) {
+ super.deleteAccount();
+ SharedPreferences defaultPrefs = getDefaultSharedPreferences();
+ deleteAppData(prefs, defaultPrefs);
+ }
+ }
+
+ // Package access for testing
+ SharedPreferences getDefaultSharedPreferences() {
+ return PreferenceManager.getDefaultSharedPreferences(appContext);
+ }
+
+ // Locking: stateChangeLock
+ private void deleteAppData(SharedPreferences... clear) {
+ // Clear and commit shared preferences
+ for (SharedPreferences prefs : clear) {
+ if (!prefs.edit().clear().commit())
+ LOG.warning("Could not clear shared preferences");
+ }
+ // Delete files, except lib and shared_prefs directories
+ File dataDir = new File(appContext.getApplicationInfo().dataDir);
+ File[] children = dataDir.listFiles();
+ if (children == null) {
+ LOG.warning("Could not list files in app data dir");
+ } else {
+ for (File child : children) {
+ String name = child.getName();
+ if (!name.equals("lib") && !name.equals("shared_prefs")) {
+ IoUtils.deleteFileOrDir(child);
+ }
+ }
+ }
+ // Recreate the cache dir as some OpenGL drivers expect it to exist
+ if (!new File(dataDir, "cache").mkdir())
+ LOG.warning("Could not recreate cache dir");
+ }
+}
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java
index c1acfff7e..3625e206e 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProvider.java
@@ -4,6 +4,8 @@ import org.briarproject.bramble.api.lifecycle.IoExecutor;
import java.util.List;
+// TODO: Create a module for this so it doesn't have to be public
+
public interface CircumventionProvider {
/**
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java
index 6a9999b9c..bf194fa4e 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/plugin/tor/CircumventionProviderImpl.java
@@ -16,6 +16,8 @@ import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject;
+// TODO: Create a module for this so it doesn't need to be public
+
public class CircumventionProviderImpl implements CircumventionProvider {
private final static String BRIDGE_FILE_NAME = "bridges";
diff --git a/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java b/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java
index 9f9dd972d..5c0d073bd 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java
@@ -3,7 +3,6 @@ package org.briarproject.bramble.util;
import android.annotation.SuppressLint;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
-import android.content.SharedPreferences;
import android.os.Build;
import android.provider.Settings;
@@ -58,30 +57,6 @@ public class AndroidUtils {
&& !address.equals(FAKE_BLUETOOTH_ADDRESS);
}
- public static void deleteAppData(Context ctx, SharedPreferences... clear) {
- // Clear and commit shared preferences
- for (SharedPreferences prefs : clear) {
- if (!prefs.edit().clear().commit())
- LOG.warning("Could not clear shared preferences");
- }
- // Delete files, except lib and shared_prefs directories
- File dataDir = new File(ctx.getApplicationInfo().dataDir);
- File[] children = dataDir.listFiles();
- if (children == null) {
- LOG.warning("Could not list files in app data dir");
- } else {
- for (File child : children) {
- String name = child.getName();
- if (!name.equals("lib") && !name.equals("shared_prefs")) {
- IoUtils.deleteFileOrDir(child);
- }
- }
- }
- // Recreate the cache dir as some OpenGL drivers expect it to exist
- if (!new File(dataDir, "cache").mkdir())
- LOG.warning("Could not recreate cache dir");
- }
-
public static File getReportDir(Context ctx) {
return ctx.getDir(STORED_REPORTS, MODE_PRIVATE);
}
diff --git a/bramble-android/src/test/java/org/briarproject/bramble/account/AndroidAccountManagerTest.java b/bramble-android/src/test/java/org/briarproject/bramble/account/AndroidAccountManagerTest.java
new file mode 100644
index 000000000..6736533a6
--- /dev/null
+++ b/bramble-android/src/test/java/org/briarproject/bramble/account/AndroidAccountManagerTest.java
@@ -0,0 +1,162 @@
+package org.briarproject.bramble.account;
+
+import android.app.Application;
+import android.content.SharedPreferences;
+import android.content.pm.ApplicationInfo;
+
+import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.test.BrambleMockTestCase;
+import org.jmock.Expectations;
+import org.jmock.lib.legacy.ClassImposteriser;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
+import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
+import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
+import static org.briarproject.bramble.util.StringUtils.toHexString;
+
+public class AndroidAccountManagerTest extends BrambleMockTestCase {
+
+ private final SharedPreferences prefs =
+ context.mock(SharedPreferences.class, "prefs");
+ private final SharedPreferences defaultPrefs =
+ context.mock(SharedPreferences.class, "defaultPrefs");
+ private final DatabaseConfig databaseConfig =
+ context.mock(DatabaseConfig.class);
+ private final CryptoComponent crypto = context.mock(CryptoComponent.class);
+ private final IdentityManager identityManager =
+ context.mock(IdentityManager.class);
+ private final SharedPreferences.Editor
+ editor = context.mock(SharedPreferences.Editor.class);
+ private final Application app;
+ private final ApplicationInfo applicationInfo;
+
+ private final String encryptedKeyHex = toHexString(getRandomBytes(123));
+ private final File testDir = getTestDirectory();
+ private final File keyDir = new File(testDir, "key");
+ private final File keyFile = new File(keyDir, "db.key");
+ private final File keyBackupFile = new File(keyDir, "db.key.bak");
+ private final File dbDir = new File(testDir, "db");
+
+ private AndroidAccountManager accountManager;
+
+ public AndroidAccountManagerTest() {
+ context.setImposteriser(ClassImposteriser.INSTANCE);
+ app = context.mock(Application.class);
+ applicationInfo = new ApplicationInfo();
+ applicationInfo.dataDir = testDir.getAbsolutePath();
+ }
+
+ @Before
+ public void setUp() {
+ context.checking(new Expectations() {{
+ allowing(databaseConfig).getDatabaseDirectory();
+ will(returnValue(dbDir));
+ allowing(databaseConfig).getDatabaseKeyDirectory();
+ will(returnValue(keyDir));
+ allowing(app).getApplicationContext();
+ will(returnValue(app));
+ }});
+ accountManager = new AndroidAccountManager(databaseConfig, crypto,
+ identityManager, prefs, app) {
+ @Override
+ SharedPreferences getDefaultSharedPreferences() {
+ return defaultPrefs;
+ }
+ };
+ }
+
+ @Test
+ public void testDbKeyIsMigratedFromPreferencesToFile() {
+ context.checking(new Expectations() {{
+ oneOf(prefs).getString("key", null);
+ will(returnValue(encryptedKeyHex));
+ oneOf(prefs).edit();
+ will(returnValue(editor));
+ oneOf(editor).remove("key");
+ will(returnValue(editor));
+ oneOf(editor).commit();
+ will(returnValue(true));
+ }});
+
+ assertFalse(keyFile.exists());
+ assertFalse(keyBackupFile.exists());
+
+ assertEquals(encryptedKeyHex,
+ accountManager.loadEncryptedDatabaseKey());
+
+ assertTrue(keyFile.exists());
+ assertTrue(keyBackupFile.exists());
+ }
+
+ @Test
+ public void testDeleteAccountClearsSharedPrefsAndDeletesFiles()
+ throws Exception {
+ // Directories 'lib' and 'shared_prefs' should be spared
+ File libDir = new File(testDir, "lib");
+ File libFile = new File(libDir, "file");
+ File sharedPrefsDir = new File(testDir, "shared_prefs");
+ File sharedPrefsFile = new File(sharedPrefsDir, "file");
+ // Directory 'cache' should be emptied
+ File cacheDir = new File(testDir, "cache");
+ File cacheFile = new File(cacheDir, "file");
+ // Other directories should be deleted
+ File potatoDir = new File(testDir, ".potato");
+ File potatoFile = new File(potatoDir, "file");
+
+ context.checking(new Expectations() {{
+ oneOf(prefs).edit();
+ will(returnValue(editor));
+ oneOf(editor).clear();
+ will(returnValue(editor));
+ oneOf(editor).commit();
+ will(returnValue(true));
+ oneOf(defaultPrefs).edit();
+ will(returnValue(editor));
+ oneOf(editor).clear();
+ will(returnValue(editor));
+ oneOf(editor).commit();
+ will(returnValue(true));
+ oneOf(app).getApplicationInfo();
+ will(returnValue(applicationInfo));
+ }});
+
+ assertTrue(dbDir.mkdirs());
+ assertTrue(keyDir.mkdirs());
+ assertTrue(libDir.mkdirs());
+ assertTrue(libFile.createNewFile());
+ assertTrue(sharedPrefsDir.mkdirs());
+ assertTrue(sharedPrefsFile.createNewFile());
+ assertTrue(cacheDir.mkdirs());
+ assertTrue(cacheFile.createNewFile());
+ assertTrue(potatoDir.mkdirs());
+ assertTrue(potatoFile.createNewFile());
+
+ accountManager.deleteAccount();
+
+ assertFalse(dbDir.exists());
+ assertFalse(keyDir.exists());
+ assertTrue(libDir.exists());
+ assertTrue(libFile.exists());
+ assertTrue(sharedPrefsDir.exists());
+ assertTrue(sharedPrefsFile.exists());
+ assertTrue(cacheDir.exists());
+ assertFalse(cacheFile.exists());
+ assertFalse(potatoDir.exists());
+ assertFalse(potatoFile.exists());
+ }
+
+ @After
+ public void tearDown() {
+ deleteTestDirectory(testDir);
+ }
+}
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java
new file mode 100644
index 000000000..2e1b5a951
--- /dev/null
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java
@@ -0,0 +1,70 @@
+package org.briarproject.bramble.api.account;
+
+import org.briarproject.bramble.api.crypto.SecretKey;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+import javax.annotation.Nullable;
+
+@NotNullByDefault
+public interface AccountManager {
+
+ /**
+ * Returns true if the manager has the database key. This will be false
+ * before {@link #createAccount(String, String)} or {@link #signIn(String)}
+ * has been called, and true after {@link #createAccount(String, String)}
+ * or {@link #signIn(String)} has returned true, until the process exits.
+ */
+ boolean hasDatabaseKey();
+
+ /**
+ * Returns the database key if the manager has it. This will be null
+ * before {@link #createAccount(String, String)} or {@link #signIn(String)}
+ * has been called, and non-null after
+ * {@link #createAccount(String, String)} or {@link #signIn(String)} has
+ * returned true, until the process exits.
+ */
+ @Nullable
+ SecretKey getDatabaseKey();
+
+ /**
+ * Returns true if the encrypted database key can be loaded from disk, and
+ * the database directory exists and is a directory.
+ */
+ boolean accountExists();
+
+ /**
+ * Creates an identity with the given name and registers it with the
+ * {@link IdentityManager}. Creates a database key, encrypts it with the
+ * given password and stores it on disk.
+ *
+ * This method does not create the database directory, so
+ * {@link #accountExists()} will continue to return false until the
+ * database directory is created.
+ */
+ boolean createAccount(String name, String password);
+
+ /**
+ * Deletes all account state from disk. {@link #accountExists()} will
+ * return false after this method returns.
+ */
+ void deleteAccount();
+
+ /**
+ * Loads the encrypted database key from disk and decrypts it with the
+ * given password.
+ *
+ * @return true if the database key was successfully loaded and decrypted.
+ */
+ boolean signIn(String password);
+
+ /**
+ * Loads the encrypted database key from disk, decrypts it with the old
+ * password, encrypts it with the new password, and stores it on disk,
+ * replacing the old key.
+ *
+ * @return true if the database key was successfully loaded, re-encrypted
+ * and stored.
+ */
+ boolean changePassword(String oldPassword, String newPassword);
+}
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
index 1f9daf48a..517dcb89f 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/contact/ContactManager.java
@@ -16,7 +16,7 @@ public interface ContactManager {
/**
* Registers a hook to be called whenever a contact is added or removed.
* This method should be called before
- * {@link LifecycleManager#startServices(String)}.
+ * {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerContactHook(ContactHook hook);
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
index 914b88a2e..f4161cee4 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
@@ -2,6 +2,7 @@ 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.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.LocalAuthor;
@@ -44,7 +45,8 @@ public interface DatabaseComponent {
* @throws DataTooOldException if the data uses an older schema than the
* current code and cannot be migrated
*/
- boolean open(@Nullable MigrationListener listener) throws DbException;
+ boolean open(SecretKey key, @Nullable MigrationListener listener)
+ throws DbException;
/**
* Waits for any open transactions to finish and closes the database.
@@ -267,7 +269,7 @@ public interface DatabaseComponent {
* Read-only.
*/
Collection getMessageIds(Transaction txn, GroupId g)
- throws DbException;
+ throws DbException;
/**
* Returns the IDs of any messages that need to be validated.
@@ -487,7 +489,7 @@ public interface DatabaseComponent {
* Removes the given transport keys from the database.
*/
void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
- throws DbException;
+ throws DbException;
/**
* Marks the given contact as verified.
@@ -534,7 +536,7 @@ public interface DatabaseComponent {
* Marks the given transport keys as usable for outgoing streams.
*/
void setTransportKeysActive(Transaction txn, TransportId t, KeySetId k)
- throws DbException;
+ throws DbException;
/**
* Stores the given transport keys, deleting any keys they have replaced.
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java
index 0be85eba9..f096f1fec 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java
@@ -1,30 +1,15 @@
package org.briarproject.bramble.api.db;
-import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
-import javax.annotation.Nullable;
-
@NotNullByDefault
public interface DatabaseConfig {
- boolean databaseExists();
-
File getDatabaseDirectory();
File getDatabaseKeyDirectory();
- void setEncryptionKey(SecretKey key);
-
- @Nullable
- SecretKey getEncryptionKey();
-
- void setLocalAuthorName(String nickname);
-
- @Nullable
- String getLocalAuthorName();
-
long getMaxSize();
}
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/IdentityManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/IdentityManager.java
index bb584fec4..8160eb06d 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/identity/IdentityManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/identity/IdentityManager.java
@@ -1,5 +1,6 @@
package org.briarproject.bramble.api.identity;
+import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author.Status;
@@ -9,29 +10,40 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
public interface IdentityManager {
/**
- * Stores the local pseudonym.
+ * Creates a local identity with the given name.
*/
- void registerLocalAuthor(LocalAuthor a) throws DbException;
+ @CryptoExecutor
+ LocalAuthor createLocalAuthor(String name);
/**
- * Returns the cached main local identity, non-blocking, or loads it from
- * the db, blocking
+ * Registers the given local identity with the manager. The identity is
+ * not stored until {@link #storeLocalAuthor()} is called.
+ */
+ void registerLocalAuthor(LocalAuthor a);
+
+ /**
+ * Stores the local identity registered with
+ * {@link #registerLocalAuthor(LocalAuthor)}, if any.
+ */
+ void storeLocalAuthor() throws DbException;
+
+ /**
+ * Returns the cached local identity or loads it from the database.
*/
LocalAuthor getLocalAuthor() throws DbException;
/**
- * Returns the cached main local identity, non-blocking, or loads it from
- * the db, blocking, within the given Transaction.
+ * Returns the cached local identity or loads it from the database.
*/
LocalAuthor getLocalAuthor(Transaction txn) throws DbException;
/**
- * Returns the trust-level status of the author
+ * Returns the {@link Status} of the given author.
*/
Status getAuthorStatus(AuthorId a) throws DbException;
/**
- * Returns the trust-level status of the author
+ * Returns the {@link Status} of the given author.
*/
Status getAuthorStatus(Transaction txn, AuthorId a) throws DbException;
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java
index d3cd1bc24..c44cf8879 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/lifecycle/LifecycleManager.java
@@ -1,13 +1,12 @@
package org.briarproject.bramble.api.lifecycle;
+import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client;
import java.util.concurrent.ExecutorService;
-import javax.annotation.Nullable;
-
/**
* Manages the lifecycle of the app, starting {@link Client Clients}, starting
* and stopping {@link Service Services}, shutting down
@@ -18,7 +17,7 @@ import javax.annotation.Nullable;
public interface LifecycleManager {
/**
- * The result of calling {@link #startServices(String)}.
+ * The result of calling {@link #startServices(SecretKey)}.
*/
enum StartResult {
ALREADY_RUNNING,
@@ -44,28 +43,27 @@ public interface LifecycleManager {
/**
* Registers a {@link Service} to be started and stopped. This method
- * should be called before {@link #startServices(String)}.
+ * should be called before {@link #startServices(SecretKey)}.
*/
void registerService(Service s);
/**
* Registers a {@link Client} to be started. This method should be called
- * before {@link #startServices(String)}.
+ * before {@link #startServices(SecretKey)}.
*/
void registerClient(Client c);
/**
* Registers an {@link ExecutorService} to be shut down. This method
- * should be called before {@link #startServices(String)}.
+ * should be called before {@link #startServices(SecretKey)}.
*/
void registerForShutdown(ExecutorService e);
/**
- * Opens the {@link DatabaseComponent}, optionally creates a local author
- * with the provided nickname, and starts any registered
- * {@link Client Clients} and {@link Service Services}.
+ * Opens the {@link DatabaseComponent} using the given key and starts any
+ * registered {@link Client Clients} and {@link Service Services}.
*/
- StartResult startServices(@Nullable String nickname);
+ StartResult startServices(SecretKey dbKey);
/**
* Stops any registered {@link Service Services}, shuts down any
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/ValidationManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/ValidationManager.java
index 1718cac81..3b5c90342 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/sync/ValidationManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/sync/ValidationManager.java
@@ -1,5 +1,6 @@
package org.briarproject.bramble.api.sync;
+import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Metadata;
import org.briarproject.bramble.api.db.Transaction;
@@ -35,7 +36,8 @@ public interface ValidationManager {
/**
* Registers the message validator for the given client. This method
- * should be called before {@link LifecycleManager#startServices(String)}.
+ * should be called before
+ * {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerMessageValidator(ClientId c, int majorVersion,
MessageValidator v);
@@ -44,7 +46,7 @@ public interface ValidationManager {
* Registers the incoming message hook for the given client. The hook will
* be called once for each incoming message that passes validation. This
* method should be called before
- * {@link LifecycleManager#startServices(String)}.
+ * {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerIncomingMessageHook(ClientId c, int majorVersion,
IncomingMessageHook hook);
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/versioning/ClientVersioningManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/versioning/ClientVersioningManager.java
index 0cb2fc478..00b3d672c 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/versioning/ClientVersioningManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/versioning/ClientVersioningManager.java
@@ -2,6 +2,7 @@ package org.briarproject.bramble.api.versioning;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
+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.lifecycle.LifecycleManager;
@@ -25,7 +26,7 @@ public interface ClientVersioningManager {
/**
* Registers a client that will be advertised to contacts. The hook will
* be called when the visibility of the client changes. This method should
- * be called before {@link LifecycleManager#startServices(String)}.
+ * be called before {@link LifecycleManager#startServices(SecretKey)}.
*/
void registerClient(ClientId clientId, int majorVersion, int minorVersion,
ClientVersioningHook hook);
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java
new file mode 100644
index 000000000..7eda4688b
--- /dev/null
+++ b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java
@@ -0,0 +1,221 @@
+package org.briarproject.bramble.account;
+
+import org.briarproject.bramble.api.account.AccountManager;
+import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.crypto.SecretKey;
+import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
+import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
+import org.briarproject.bramble.util.IoUtils;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.logging.Logger;
+
+import javax.annotation.Nullable;
+import javax.inject.Inject;
+
+import static java.util.logging.Level.WARNING;
+import static org.briarproject.bramble.util.LogUtils.logException;
+import static org.briarproject.bramble.util.StringUtils.fromHexString;
+import static org.briarproject.bramble.util.StringUtils.toHexString;
+
+@MethodsNotNullByDefault
+@ParametersNotNullByDefault
+class AccountManagerImpl implements AccountManager {
+
+ private static final Logger LOG =
+ Logger.getLogger(AccountManagerImpl.class.getName());
+
+ private static final String DB_KEY_FILENAME = "db.key";
+ private static final String DB_KEY_BACKUP_FILENAME = "db.key.bak";
+
+ private final DatabaseConfig databaseConfig;
+ private final CryptoComponent crypto;
+ private final IdentityManager identityManager;
+ private final File dbKeyFile, dbKeyBackupFile;
+
+ final Object stateChangeLock = new Object();
+
+ @Nullable
+ private volatile SecretKey databaseKey = null;
+
+ @Inject
+ AccountManagerImpl(DatabaseConfig databaseConfig, CryptoComponent crypto,
+ IdentityManager identityManager) {
+ this.databaseConfig = databaseConfig;
+ this.crypto = crypto;
+ this.identityManager = identityManager;
+ File keyDir = databaseConfig.getDatabaseKeyDirectory();
+ dbKeyFile = new File(keyDir, DB_KEY_FILENAME);
+ dbKeyBackupFile = new File(keyDir, DB_KEY_BACKUP_FILENAME);
+ }
+
+ @Override
+ public boolean hasDatabaseKey() {
+ return databaseKey != null;
+ }
+
+ @Override
+ @Nullable
+ public SecretKey getDatabaseKey() {
+ return databaseKey;
+ }
+
+ // Locking: stateChangeLock
+ @Nullable
+ protected String loadEncryptedDatabaseKey() {
+ String key = readDbKeyFromFile(dbKeyFile);
+ if (key == null) {
+ LOG.info("No database key in primary file");
+ key = readDbKeyFromFile(dbKeyBackupFile);
+ if (key == null) LOG.info("No database key in backup file");
+ else LOG.warning("Found database key in backup file");
+ } else {
+ LOG.info("Found database key in primary file");
+ }
+ return key;
+ }
+
+ // Locking: stateChangeLock
+ @Nullable
+ private String readDbKeyFromFile(File f) {
+ if (!f.exists()) {
+ LOG.info("Key file does not exist");
+ return null;
+ }
+ try {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ new FileInputStream(f), "UTF-8"));
+ String key = reader.readLine();
+ reader.close();
+ return key;
+ } catch (IOException e) {
+ logException(LOG, WARNING, e);
+ return null;
+ }
+ }
+
+ // Locking: stateChangeLock
+ protected boolean storeEncryptedDatabaseKey(String hex) {
+ LOG.info("Storing database key in file");
+ // Create the directory if necessary
+ if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
+ LOG.info("Created database key directory");
+ // If only the backup file exists, rename it so we don't overwrite it
+ if (dbKeyBackupFile.exists() && !dbKeyFile.exists()) {
+ if (dbKeyBackupFile.renameTo(dbKeyFile))
+ LOG.info("Renamed old backup");
+ else LOG.warning("Failed to rename old backup");
+ }
+ try {
+ // Write to the backup file
+ writeDbKeyToFile(hex, dbKeyBackupFile);
+ LOG.info("Stored database key in backup file");
+ // Delete the old primary file, if it exists
+ if (dbKeyFile.exists()) {
+ if (dbKeyFile.delete()) LOG.info("Deleted primary file");
+ else LOG.warning("Failed to delete primary file");
+ }
+ // The backup file becomes the new primary
+ if (dbKeyBackupFile.renameTo(dbKeyFile)) {
+ LOG.info("Renamed backup file to primary");
+ } else {
+ LOG.warning("Failed to rename backup file to primary");
+ return false; // Don't overwrite our only copy
+ }
+ // Write a second copy to the backup file
+ writeDbKeyToFile(hex, dbKeyBackupFile);
+ LOG.info("Stored second copy of database key in backup file");
+ return true;
+ } catch (IOException e) {
+ logException(LOG, WARNING, e);
+ return false;
+ }
+ }
+
+ // Locking: stateChangeLock
+ private void writeDbKeyToFile(String key, File f) throws IOException {
+ FileOutputStream out = new FileOutputStream(f);
+ out.write(key.getBytes("UTF-8"));
+ out.flush();
+ out.close();
+ }
+
+ @Override
+ public boolean accountExists() {
+ synchronized (stateChangeLock) {
+ return loadEncryptedDatabaseKey() != null
+ && databaseConfig.getDatabaseDirectory().isDirectory();
+ }
+ }
+
+ @Override
+ public boolean createAccount(String name, String password) {
+ synchronized (stateChangeLock) {
+ LocalAuthor localAuthor = identityManager.createLocalAuthor(name);
+ identityManager.registerLocalAuthor(localAuthor);
+ SecretKey key = crypto.generateSecretKey();
+ if (!encryptAndStoreDatabaseKey(key, password)) return false;
+ databaseKey = key;
+ return true;
+ }
+ }
+
+ // Locking: stateChangeLock
+ private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
+ byte[] plaintext = key.getBytes();
+ byte[] ciphertext = crypto.encryptWithPassword(plaintext, password);
+ return storeEncryptedDatabaseKey(toHexString(ciphertext));
+ }
+
+ @Override
+ public void deleteAccount() {
+ synchronized (stateChangeLock) {
+ LOG.info("Deleting account");
+ IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory());
+ IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory());
+ }
+ }
+
+ @Override
+ public boolean signIn(String password) {
+ synchronized (stateChangeLock) {
+ SecretKey key = loadAndDecryptDatabaseKey(password);
+ if (key == null) return false;
+ databaseKey = key;
+ return true;
+ }
+ }
+
+ // Locking: stateChangeLock
+ @Nullable
+ private SecretKey loadAndDecryptDatabaseKey(String password) {
+ String hex = loadEncryptedDatabaseKey();
+ if (hex == null) {
+ LOG.warning("Failed to load encrypted database key");
+ return null;
+ }
+ byte[] ciphertext = fromHexString(hex);
+ byte[] plaintext = crypto.decryptWithPassword(ciphertext, password);
+ if (plaintext == null) {
+ LOG.info("Failed to decrypt database key");
+ return null;
+ }
+ return new SecretKey(plaintext);
+ }
+
+ @Override
+ public boolean changePassword(String oldPassword, String newPassword) {
+ synchronized (stateChangeLock) {
+ SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
+ return key != null && encryptAndStoreDatabaseKey(key, newPassword);
+ }
+ }
+}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountModule.java b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountModule.java
new file mode 100644
index 000000000..e8220176a
--- /dev/null
+++ b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountModule.java
@@ -0,0 +1,18 @@
+package org.briarproject.bramble.account;
+
+import org.briarproject.bramble.api.account.AccountManager;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class AccountModule {
+
+ @Provides
+ @Singleton
+ AccountManager provideAccountManager(AccountManagerImpl accountManager) {
+ return accountManager;
+ }
+}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
index 66e5cfcf6..796e4bbe4 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
@@ -2,6 +2,7 @@ package org.briarproject.bramble.db;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
+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.DbException;
@@ -48,7 +49,8 @@ interface Database {
* @throws DataTooOldException if the data uses an older schema than the
* current code and cannot be migrated
*/
- boolean open(@Nullable MigrationListener listener) throws DbException;
+ boolean open(SecretKey key, @Nullable MigrationListener listener)
+ throws DbException;
/**
* Prevents new transactions from starting, waits for all current
@@ -641,7 +643,7 @@ interface Database {
* Marks the given transport keys as usable for outgoing streams.
*/
void setTransportKeysActive(T txn, TransportId t, KeySetId k)
- throws DbException;
+ throws DbException;
/**
* Updates the transmission count and expiry time of the given message
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
index f2e4de1c6..2913c783f 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
@@ -6,6 +6,7 @@ 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.ContactExistsException;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
@@ -103,9 +104,9 @@ class DatabaseComponentImpl implements DatabaseComponent {
}
@Override
- public boolean open(@Nullable MigrationListener listener)
+ public boolean open(SecretKey key, @Nullable MigrationListener listener)
throws DbException {
- boolean reopened = db.open(listener);
+ boolean reopened = db.open(key, listener);
shutdown.addShutdownHook(() -> {
try {
close();
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java
index 1c1983e67..f400d1ce9 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/H2Database.java
@@ -32,6 +32,9 @@ class H2Database extends JdbcDatabase {
private final DatabaseConfig config;
private final String url;
+ @Nullable
+ private volatile SecretKey key = null;
+
@Inject
H2Database(DatabaseConfig config, Clock clock) {
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
@@ -44,11 +47,11 @@ class H2Database extends JdbcDatabase {
}
@Override
- public boolean open(@Nullable MigrationListener listener)
+ public boolean open(SecretKey key, @Nullable MigrationListener listener)
throws DbException {
- boolean reopen = config.databaseExists();
- if (!reopen) config.getDatabaseDirectory().mkdirs();
- super.open("org.h2.Driver", reopen, listener);
+ this.key = key;
+ boolean reopen = !config.getDatabaseDirectory().mkdirs();
+ super.open("org.h2.Driver", reopen, key, listener);
return reopen;
}
@@ -63,7 +66,7 @@ class H2Database extends JdbcDatabase {
}
@Override
- public long getFreeSpace() throws DbException {
+ public long getFreeSpace() {
File dir = config.getDatabaseDirectory();
long maxSize = config.getMaxSize();
long free = dir.getFreeSpace();
@@ -88,7 +91,7 @@ class H2Database extends JdbcDatabase {
@Override
protected Connection createConnection() throws SQLException {
- SecretKey key = config.getEncryptionKey();
+ SecretKey key = this.key;
if (key == null) throw new IllegalStateException();
Properties props = new Properties();
props.setProperty("user", "user");
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java
index 6a87ededa..447537d8c 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/HyperSqlDatabase.java
@@ -33,6 +33,9 @@ class HyperSqlDatabase extends JdbcDatabase {
private final DatabaseConfig config;
private final String url;
+ @Nullable
+ private volatile SecretKey key = null;
+
@Inject
HyperSqlDatabase(DatabaseConfig config, Clock clock) {
super(HASH_TYPE, SECRET_TYPE, BINARY_TYPE, COUNTER_TYPE, STRING_TYPE,
@@ -46,10 +49,11 @@ class HyperSqlDatabase extends JdbcDatabase {
}
@Override
- public boolean open(@Nullable MigrationListener listener) throws DbException {
- boolean reopen = config.databaseExists();
- if (!reopen) config.getDatabaseDirectory().mkdirs();
- super.open("org.hsqldb.jdbc.JDBCDriver", reopen, listener);
+ public boolean open(SecretKey key, @Nullable MigrationListener listener)
+ throws DbException {
+ this.key = key;
+ boolean reopen = !config.getDatabaseDirectory().mkdirs();
+ super.open("org.hsqldb.jdbc.JDBCDriver", reopen, key, listener);
return reopen;
}
@@ -93,7 +97,7 @@ class HyperSqlDatabase extends JdbcDatabase {
@Override
protected Connection createConnection() throws SQLException {
- SecretKey key = config.getEncryptionKey();
+ SecretKey key = this.key;
if (key == null) throw new IllegalStateException();
String hex = StringUtils.toHexString(key.getBytes());
return DriverManager.getConnection(url + ";crypt_key=" + hex);
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
index baa67a29a..fd248f811 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
@@ -328,7 +328,7 @@ abstract class JdbcDatabase implements Database {
this.clock = clock;
}
- protected void open(String driverClass, boolean reopen,
+ protected void open(String driverClass, boolean reopen, SecretKey key,
@Nullable MigrationListener listener) throws DbException {
// Load the JDBC driver
try {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/identity/IdentityManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/identity/IdentityManagerImpl.java
index 11e8220c9..44bf94550 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/identity/IdentityManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/identity/IdentityManagerImpl.java
@@ -1,10 +1,13 @@
package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.contact.Contact;
+import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author.Status;
+import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
@@ -21,6 +24,8 @@ import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.bramble.api.identity.Author.Status.UNVERIFIED;
import static org.briarproject.bramble.api.identity.Author.Status.VERIFIED;
+import static org.briarproject.bramble.util.LogUtils.logDuration;
+import static org.briarproject.bramble.util.LogUtils.now;
@ThreadSafe
@NotNullByDefault
@@ -30,25 +35,51 @@ class IdentityManagerImpl implements IdentityManager {
Logger.getLogger(IdentityManagerImpl.class.getName());
private final DatabaseComponent db;
+ private final CryptoComponent crypto;
+ private final AuthorFactory authorFactory;
// The local author is immutable so we can cache it
@Nullable
private volatile LocalAuthor cachedAuthor;
@Inject
- IdentityManagerImpl(DatabaseComponent db) {
+ IdentityManagerImpl(DatabaseComponent db, CryptoComponent crypto,
+ AuthorFactory authorFactory) {
this.db = db;
+ this.crypto = crypto;
+ this.authorFactory = authorFactory;
}
@Override
- public void registerLocalAuthor(LocalAuthor localAuthor)
- throws DbException {
+ public LocalAuthor createLocalAuthor(String name) {
+ long start = now();
+ KeyPair keyPair = crypto.generateSignatureKeyPair();
+ byte[] publicKey = keyPair.getPublic().getEncoded();
+ byte[] privateKey = keyPair.getPrivate().getEncoded();
+ LocalAuthor localAuthor = authorFactory.createLocalAuthor(name,
+ publicKey, privateKey);
+ logDuration(LOG, "Creating local author", start);
+ return localAuthor;
+ }
+
+ @Override
+ public void registerLocalAuthor(LocalAuthor a) {
+ cachedAuthor = a;
+ LOG.info("Local author registered");
+ }
+
+ @Override
+ public void storeLocalAuthor() throws DbException {
+ LocalAuthor cached = cachedAuthor;
+ if (cached == null) {
+ LOG.info("No local author to store");
+ return;
+ }
Transaction txn = db.startTransaction(false);
try {
- db.addLocalAuthor(txn, localAuthor);
+ db.addLocalAuthor(txn, cached);
db.commitTransaction(txn);
- cachedAuthor = localAuthor;
- LOG.info("Local author registered");
+ LOG.info("Local author stored");
} finally {
db.endTransaction(txn);
}
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java
index 68c694189..130595ab6 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleManagerImpl.java
@@ -1,7 +1,6 @@
package org.briarproject.bramble.lifecycle;
-import org.briarproject.bramble.api.crypto.CryptoComponent;
-import org.briarproject.bramble.api.crypto.KeyPair;
+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;
@@ -9,9 +8,7 @@ import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.MigrationListener;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.event.EventBus;
-import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.IdentityManager;
-import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.lifecycle.Service;
import org.briarproject.bramble.api.lifecycle.ServiceException;
@@ -26,7 +23,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
-import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject;
@@ -60,8 +56,6 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
private final List services;
private final List clients;
private final List executors;
- private final CryptoComponent crypto;
- private final AuthorFactory authorFactory;
private final IdentityManager identityManager;
private final Semaphore startStopSemaphore = new Semaphore(1);
private final CountDownLatch dbLatch = new CountDownLatch(1);
@@ -72,12 +66,9 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
@Inject
LifecycleManagerImpl(DatabaseComponent db, EventBus eventBus,
- CryptoComponent crypto, AuthorFactory authorFactory,
IdentityManager identityManager) {
this.db = db;
this.eventBus = eventBus;
- this.crypto = crypto;
- this.authorFactory = authorFactory;
this.identityManager = identityManager;
services = new CopyOnWriteArrayList<>();
clients = new CopyOnWriteArrayList<>();
@@ -104,25 +95,8 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
executors.add(e);
}
- private LocalAuthor createLocalAuthor(String nickname) {
- long start = now();
- KeyPair keyPair = crypto.generateSignatureKeyPair();
- byte[] publicKey = keyPair.getPublic().getEncoded();
- byte[] privateKey = keyPair.getPrivate().getEncoded();
- LocalAuthor localAuthor = authorFactory
- .createLocalAuthor(nickname, publicKey, privateKey);
- logDuration(LOG, "Creating local author", start);
- return localAuthor;
- }
-
- private void registerLocalAuthor(LocalAuthor author) throws DbException {
- long start = now();
- identityManager.registerLocalAuthor(author);
- logDuration(LOG, "Registering local author", start);
- }
-
@Override
- public StartResult startServices(@Nullable String nickname) {
+ public StartResult startServices(SecretKey dbKey) {
if (!startStopSemaphore.tryAcquire()) {
LOG.info("Already starting or stopping");
return ALREADY_RUNNING;
@@ -131,13 +105,10 @@ class LifecycleManagerImpl implements LifecycleManager, MigrationListener {
LOG.info("Starting services");
long start = now();
- boolean reopened = db.open(this);
+ boolean reopened = db.open(dbKey, this);
if (reopened) logDuration(LOG, "Reopening database", start);
else logDuration(LOG, "Creating database", start);
-
- if (nickname != null) {
- registerLocalAuthor(createLocalAuthor(nickname));
- }
+ identityManager.storeLocalAuthor();
state = STARTING_SERVICES;
dbLatch.countDown();
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleModule.java b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleModule.java
index 8fcf789ed..43db266ef 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleModule.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/lifecycle/LifecycleModule.java
@@ -1,10 +1,5 @@
package org.briarproject.bramble.lifecycle;
-import org.briarproject.bramble.api.crypto.CryptoComponent;
-import org.briarproject.bramble.api.db.DatabaseComponent;
-import org.briarproject.bramble.api.event.EventBus;
-import org.briarproject.bramble.api.identity.AuthorFactory;
-import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.lifecycle.ShutdownManager;
@@ -54,11 +49,9 @@ public class LifecycleModule {
@Provides
@Singleton
- LifecycleManager provideLifecycleManager(DatabaseComponent db,
- EventBus eventBus, CryptoComponent crypto,
- AuthorFactory authorFactory, IdentityManager identityManager) {
- return new LifecycleManagerImpl(db, eventBus, crypto, authorFactory,
- identityManager);
+ LifecycleManager provideLifecycleManager(
+ LifecycleManagerImpl lifecycleManager) {
+ return lifecycleManager;
}
@Provides
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/account/AccountManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/account/AccountManagerImplTest.java
new file mode 100644
index 000000000..84c5da72f
--- /dev/null
+++ b/bramble-core/src/test/java/org/briarproject/bramble/account/AccountManagerImplTest.java
@@ -0,0 +1,341 @@
+package org.briarproject.bramble.account;
+
+import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.crypto.SecretKey;
+import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.identity.LocalAuthor;
+import org.briarproject.bramble.test.BrambleMockTestCase;
+import org.jmock.Expectations;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import javax.annotation.Nullable;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
+import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
+import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
+import static org.briarproject.bramble.test.TestUtils.getSecretKey;
+import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
+import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.bramble.util.StringUtils.toHexString;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class AccountManagerImplTest extends BrambleMockTestCase {
+
+ private final DatabaseConfig databaseConfig =
+ context.mock(DatabaseConfig.class);
+ private final CryptoComponent crypto = context.mock(CryptoComponent.class);
+ private final IdentityManager identityManager =
+ context.mock(IdentityManager.class);
+
+ private final SecretKey key = getSecretKey();
+ private final byte[] encryptedKey = getRandomBytes(123);
+ private final String encryptedKeyHex = toHexString(encryptedKey);
+ private final byte[] newEncryptedKey = getRandomBytes(123);
+ private final String newEncryptedKeyHex = toHexString(newEncryptedKey);
+ private final LocalAuthor localAuthor = getLocalAuthor();
+ private final String authorName = localAuthor.getName();
+ private final String password = getRandomString(10);
+ private final String newPassword = getRandomString(10);
+ private final File testDir = getTestDirectory();
+ private final File dbDir = new File(testDir, "db");
+ private final File keyDir = new File(testDir, "key");
+ private final File keyFile = new File(keyDir, "db.key");
+ private final File keyBackupFile = new File(keyDir, "db.key.bak");
+
+ private AccountManagerImpl accountManager;
+
+ @Before
+ public void setUp() {
+ context.checking(new Expectations() {{
+ allowing(databaseConfig).getDatabaseDirectory();
+ will(returnValue(dbDir));
+ allowing(databaseConfig).getDatabaseKeyDirectory();
+ will(returnValue(keyDir));
+ }});
+
+ accountManager =
+ new AccountManagerImpl(databaseConfig, crypto, identityManager);
+
+ assertFalse(keyFile.exists());
+ assertFalse(keyBackupFile.exists());
+ }
+
+ @Test
+ public void testSignInReturnsFalseIfDbKeyCannotBeLoaded() {
+ assertFalse(accountManager.signIn(password));
+ assertFalse(accountManager.hasDatabaseKey());
+
+ assertFalse(keyFile.exists());
+ assertFalse(keyBackupFile.exists());
+ }
+
+ @Test
+ public void testSignInReturnsFalseIfPasswordIsWrong() throws Exception {
+ context.checking(new Expectations() {{
+ oneOf(crypto).decryptWithPassword(encryptedKey, password);
+ will(returnValue(null));
+ }});
+
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+
+ assertFalse(accountManager.signIn(password));
+ assertFalse(accountManager.hasDatabaseKey());
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ }
+
+ @Test
+ public void testSignInReturnsTrueIfPasswordIsRight() throws Exception {
+ context.checking(new Expectations() {{
+ oneOf(crypto).decryptWithPassword(encryptedKey, password);
+ will(returnValue(key.getBytes()));
+ }});
+
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+
+ assertTrue(accountManager.signIn(password));
+ assertTrue(accountManager.hasDatabaseKey());
+ SecretKey decrypted = accountManager.getDatabaseKey();
+ assertNotNull(decrypted);
+ assertArrayEquals(key.getBytes(), decrypted.getBytes());
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ }
+
+ @Test
+ public void testDbKeyIsLoadedFromPrimaryFile() throws Exception {
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertFalse(keyBackupFile.exists());
+
+ assertEquals(encryptedKeyHex,
+ accountManager.loadEncryptedDatabaseKey());
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertFalse(keyBackupFile.exists());
+ }
+
+ @Test
+ public void testDbKeyIsLoadedFromBackupFile() throws Exception {
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertFalse(keyFile.exists());
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+
+ assertEquals(encryptedKeyHex,
+ accountManager.loadEncryptedDatabaseKey());
+
+ assertFalse(keyFile.exists());
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ }
+
+ @Test
+ public void testDbKeyIsNullIfNotFound() {
+ assertNull(accountManager.loadEncryptedDatabaseKey());
+
+ assertFalse(keyFile.exists());
+ assertFalse(keyBackupFile.exists());
+ }
+
+ @Test
+ public void testStoringDbKeyOverwritesPrimary() throws Exception {
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertFalse(keyBackupFile.exists());
+
+ assertTrue(accountManager.storeEncryptedDatabaseKey(
+ newEncryptedKeyHex));
+
+ assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ }
+
+ @Test
+ public void testStoringDbKeyOverwritesBackup() throws Exception {
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertFalse(keyFile.exists());
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+
+ assertTrue(accountManager.storeEncryptedDatabaseKey(
+ newEncryptedKeyHex));
+
+ assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ }
+
+ @Test
+ public void testAccountExistsReturnsFalseIfDbKeyCannotBeLoaded() {
+ assertFalse(accountManager.accountExists());
+
+ assertFalse(keyFile.exists());
+ assertFalse(keyBackupFile.exists());
+ }
+
+ @Test
+ public void testAccountExistsReturnsFalseIfDbDirectoryDoesNotExist()
+ throws Exception {
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertFalse(dbDir.exists());
+
+ assertFalse(accountManager.accountExists());
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ assertFalse(dbDir.exists());
+ }
+
+ @Test
+ public void testAccountExistsReturnsFalseIfDbDirectoryIsNotDirectory()
+ throws Exception {
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertTrue(dbDir.createNewFile());
+ assertFalse(dbDir.isDirectory());
+
+ assertFalse(accountManager.accountExists());
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ assertTrue(dbDir.exists());
+ assertFalse(dbDir.isDirectory());
+ }
+
+ @Test
+ public void testAccountExistsReturnsTrueIfDbDirectoryIsDirectory()
+ throws Exception {
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertTrue(dbDir.mkdirs());
+ assertTrue(dbDir.isDirectory());
+
+ assertTrue(accountManager.accountExists());
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ assertTrue(dbDir.exists());
+ assertTrue(dbDir.isDirectory());
+ }
+
+ @Test
+ public void testCreateAccountStoresDbKey() throws Exception {
+ context.checking(new Expectations() {{
+ oneOf(identityManager).createLocalAuthor(authorName);
+ will(returnValue(localAuthor));
+ oneOf(identityManager).registerLocalAuthor(localAuthor);
+ oneOf(crypto).generateSecretKey();
+ will(returnValue(key));
+ oneOf(crypto).encryptWithPassword(key.getBytes(), password);
+ will(returnValue(encryptedKey));
+ }});
+
+ assertFalse(accountManager.hasDatabaseKey());
+
+ assertTrue(accountManager.createAccount(authorName, password));
+
+ assertTrue(accountManager.hasDatabaseKey());
+ SecretKey dbKey = accountManager.getDatabaseKey();
+ assertNotNull(dbKey);
+ assertArrayEquals(key.getBytes(), dbKey.getBytes());
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ }
+
+ @Test
+ public void testChangePasswordReturnsFalseIfDbKeyCannotBeLoaded() {
+ assertFalse(accountManager.changePassword(password, newPassword));
+
+ assertFalse(keyFile.exists());
+ assertFalse(keyBackupFile.exists());
+ }
+
+ @Test
+ public void testChangePasswordReturnsFalseIfPasswordIsWrong()
+ throws Exception {
+ context.checking(new Expectations() {{
+ oneOf(crypto).decryptWithPassword(encryptedKey, password);
+ will(returnValue(null));
+ }});
+
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertFalse(accountManager.changePassword(password, newPassword));
+
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ }
+
+ @Test
+ public void testChangePasswordReturnsTrueIfPasswordIsRight()
+ throws Exception {
+ context.checking(new Expectations() {{
+ oneOf(crypto).decryptWithPassword(encryptedKey, password);
+ will(returnValue(key.getBytes()));
+ oneOf(crypto).encryptWithPassword(key.getBytes(), newPassword);
+ will(returnValue(newEncryptedKey));
+ }});
+
+ storeDatabaseKey(keyFile, encryptedKeyHex);
+ storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+ assertTrue(accountManager.changePassword(password, newPassword));
+
+ assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyFile));
+ assertEquals(newEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
+ }
+
+ private void storeDatabaseKey(File f, String hex) throws IOException {
+ f.getParentFile().mkdirs();
+ FileOutputStream out = new FileOutputStream(f);
+ out.write(hex.getBytes("UTF-8"));
+ out.flush();
+ out.close();
+ }
+
+ @Nullable
+ private String loadDatabaseKey(File f) throws IOException {
+ BufferedReader reader = new BufferedReader(new InputStreamReader(
+ new FileInputStream(f), "UTF-8"));
+ String hex = reader.readLine();
+ reader.close();
+ return hex;
+ }
+
+ @After
+ public void tearDown() {
+ deleteTestDirectory(testDir);
+ }
+}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java
index e16e03da0..676cac2e3 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseComponentImplTest.java
@@ -89,6 +89,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
context.mock(ShutdownManager.class);
private final EventBus eventBus = context.mock(EventBus.class);
+ private final SecretKey key = getSecretKey();
private final Object txn = new Object();
private final ClientId clientId;
private final int majorVersion;
@@ -141,7 +142,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
int shutdownHandle = 12345;
context.checking(new Expectations() {{
// open()
- oneOf(database).open(null);
+ oneOf(database).open(key, null);
will(returnValue(false));
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle));
@@ -208,7 +209,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
Transaction transaction = db.startTransaction(false);
try {
db.addLocalAuthor(transaction, localAuthor);
@@ -1602,7 +1603,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
MessageId messageId2 = new MessageId(getRandomId());
context.checking(new Expectations() {{
// open()
- oneOf(database).open(null);
+ oneOf(database).open(key, null);
will(returnValue(false));
oneOf(shutdown).addShutdownHook(with(any(Runnable.class)));
will(returnValue(shutdownHandle));
@@ -1646,7 +1647,7 @@ public class DatabaseComponentImplTest extends BrambleMockTestCase {
DatabaseComponent db = createDatabaseComponent(database, eventBus,
shutdown);
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
Transaction transaction = db.startTransaction(false);
try {
db.addLocalMessage(transaction, message, metadata, true);
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java
index 89bdf8a4c..0740bdfc2 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseMigrationTest.java
@@ -1,5 +1,6 @@
package org.briarproject.bramble.db;
+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.DatabaseConfig;
@@ -26,6 +27,7 @@ import static java.util.Collections.singletonList;
import static org.briarproject.bramble.db.DatabaseConstants.DB_SETTINGS_NAMESPACE;
import static org.briarproject.bramble.db.DatabaseConstants.SCHEMA_VERSION_KEY;
import static org.briarproject.bramble.db.JdbcDatabase.CODE_SCHEMA_VERSION;
+import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
@@ -43,6 +45,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
protected final DatabaseConfig config =
new TestDatabaseConfig(testDir, 1024 * 1024);
+ protected final SecretKey key = getSecretKey();
protected final Clock clock = new SystemClock();
abstract Database createDatabase(
@@ -62,7 +65,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
public void testDoesNotRunMigrationsWhenCreatingDatabase()
throws Exception {
Database db = createDatabase(singletonList(migration));
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
db.close();
}
@@ -72,14 +75,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
throws Exception {
// Open the DB for the first time
Database db = createDatabase(asList(migration, migration1));
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
// Override the data schema version
setDataSchemaVersion(db, -1);
db.close();
// Reopen the DB - an exception should be thrown
db = createDatabase(asList(migration, migration1));
- db.open(null);
+ db.open(key, null);
}
@Test
@@ -87,12 +90,12 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
throws Exception {
// Open the DB for the first time
Database db = createDatabase(asList(migration, migration1));
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
db.close();
// Reopen the DB - migrations should not be run
db = createDatabase(asList(migration, migration1));
- assertTrue(db.open(null));
+ assertTrue(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
db.close();
}
@@ -101,14 +104,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
public void testThrowsExceptionIfDataIsNewerThanCode() throws Exception {
// Open the DB for the first time
Database db = createDatabase(asList(migration, migration1));
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
// Override the data schema version
setDataSchemaVersion(db, CODE_SCHEMA_VERSION + 1);
db.close();
// Reopen the DB - an exception should be thrown
db = createDatabase(asList(migration, migration1));
- db.open(null);
+ db.open(key, null);
}
@Test(expected = DataTooOldException.class)
@@ -116,13 +119,13 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
throws Exception {
// Open the DB for the first time
Database db = createDatabase(emptyList());
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 1);
db.close();
// Reopen the DB - an exception should be thrown
db = createDatabase(emptyList());
- db.open(null);
+ db.open(key, null);
}
@Test(expected = DataTooOldException.class)
@@ -141,14 +144,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
// Open the DB for the first time
Database db = createDatabase(asList(migration, migration1));
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
// Override the data schema version
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 3);
db.close();
// Reopen the DB - an exception should be thrown
db = createDatabase(asList(migration, migration1));
- db.open(null);
+ db.open(key, null);
}
@Test
@@ -170,14 +173,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
// Open the DB for the first time
Database db = createDatabase(asList(migration, migration1));
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
// Override the data schema version
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2);
db.close();
// Reopen the DB - the first migration should be run
db = createDatabase(asList(migration, migration1));
- assertTrue(db.open(null));
+ assertTrue(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
db.close();
}
@@ -202,14 +205,14 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase {
// Open the DB for the first time
Database db = createDatabase(asList(migration, migration1));
- assertFalse(db.open(null));
+ assertFalse(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
// Override the data schema version
setDataSchemaVersion(db, CODE_SCHEMA_VERSION - 2);
db.close();
// Reopen the DB - both migrations should be run
db = createDatabase(asList(migration, migration1));
- assertTrue(db.open(null));
+ assertTrue(db.open(key, null));
assertEquals(CODE_SCHEMA_VERSION, getDataSchemaVersion(db));
db.close();
}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java
index 1807d30dc..279d1b56e 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabasePerformanceComparisonTest.java
@@ -15,6 +15,7 @@ import java.util.List;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getMean;
import static org.briarproject.bramble.test.TestUtils.getMedian;
+import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getStandardDeviation;
import static org.briarproject.bramble.test.UTest.Z_CRITICAL_0_01;
@@ -71,7 +72,7 @@ public abstract class DatabasePerformanceComparisonTest
throws DbException {
Database db = createDatabase(conditionA,
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
- db.open(null);
+ db.open(getSecretKey(), null);
return db;
}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java
index f4e0daa19..2060ca71e 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/DatabaseTraceTest.java
@@ -16,6 +16,7 @@ import java.sql.Connection;
import javax.annotation.Nullable;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
+import static org.briarproject.bramble.test.TestUtils.getSecretKey;
public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
@@ -43,7 +44,7 @@ public abstract class DatabaseTraceTest extends DatabasePerformanceTest {
private Database openDatabase() throws DbException {
Database db = createDatabase(
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
- db.open(null);
+ db.open(getSecretKey(), null);
return db;
}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/H2MigrationTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/H2MigrationTest.java
index 6a868fd42..29bdbfa5f 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/H2MigrationTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/H2MigrationTest.java
@@ -9,8 +9,8 @@ import java.util.List;
public class H2MigrationTest extends DatabaseMigrationTest {
@Override
- Database createDatabase(List> migrations)
- throws Exception {
+ Database createDatabase(
+ List> migrations) {
return new H2Database(config, clock) {
@Override
List> getMigrations() {
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
index 2655e9ef7..e88038258 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/JdbcDatabaseTest.java
@@ -64,6 +64,7 @@ import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.briarproject.bramble.test.TestUtils.getRandomId;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
+import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -79,7 +80,8 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private static final int ONE_MEGABYTE = 1024 * 1024;
private static final int MAX_SIZE = 5 * ONE_MEGABYTE;
- private final File testDir = TestUtils.getTestDirectory();
+ private final SecretKey key = getSecretKey();
+ private final File testDir = getTestDirectory();
private final GroupId groupId;
private final ClientId clientId;
private final int majorVersion;
@@ -96,7 +98,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
private final KeySetId keySetId, keySetId1;
private final Random random = new Random();
- JdbcDatabaseTest() throws Exception {
+ JdbcDatabaseTest() {
clientId = getClientId();
majorVersion = 123;
group = getGroup(clientId, majorVersion);
@@ -1819,7 +1821,7 @@ public abstract class JdbcDatabaseTest extends BrambleTestCase {
Database db = createDatabase(
new TestDatabaseConfig(testDir, MAX_SIZE), clock);
if (!resume) TestUtils.deleteTestDirectory(testDir);
- db.open(null);
+ db.open(key, null);
return db;
}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java b/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java
index 670c7cddb..6256d39cd 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/db/SingleDatabasePerformanceTest.java
@@ -13,6 +13,7 @@ import java.util.List;
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getMean;
import static org.briarproject.bramble.test.TestUtils.getMedian;
+import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getStandardDeviation;
public abstract class SingleDatabasePerformanceTest
@@ -40,7 +41,7 @@ public abstract class SingleDatabasePerformanceTest
private Database openDatabase() throws DbException {
Database db = createDatabase(
new TestDatabaseConfig(testDir, MAX_SIZE), new SystemClock());
- db.open(null);
+ db.open(getSecretKey(), null);
return db;
}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/identity/IdentityManagerImplTest.java b/bramble-core/src/test/java/org/briarproject/bramble/identity/IdentityManagerImplTest.java
index e24499ba7..2926cd3f3 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/identity/IdentityManagerImplTest.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/identity/IdentityManagerImplTest.java
@@ -2,15 +2,21 @@ package org.briarproject.bramble.identity;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
+import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.crypto.KeyPair;
+import org.briarproject.bramble.api.crypto.PrivateKey;
+import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
+import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
+import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
@@ -27,24 +33,48 @@ import static org.junit.Assert.assertEquals;
public class IdentityManagerImplTest extends BrambleMockTestCase {
- private final IdentityManager identityManager;
private final DatabaseComponent db = context.mock(DatabaseComponent.class);
+ private final CryptoComponent crypto = context.mock(CryptoComponent.class);
+ private final AuthorFactory authorFactory =
+ context.mock(AuthorFactory.class);
+ private final PublicKey publicKey = context.mock(PublicKey.class);
+ private final PrivateKey privateKey = context.mock(PrivateKey.class);
+
private final Transaction txn = new Transaction(null, false);
private final LocalAuthor localAuthor = getLocalAuthor();
private final Collection localAuthors =
Collections.singletonList(localAuthor);
+ private final String authorName = localAuthor.getName();
+ private final KeyPair keyPair = new KeyPair(publicKey, privateKey);
+ private final byte[] publicKeyBytes = localAuthor.getPublicKey();
+ private final byte[] privateKeyBytes = localAuthor.getPrivateKey();
+ private IdentityManager identityManager;
- public IdentityManagerImplTest() {
- identityManager = new IdentityManagerImpl(db);
+ @Before
+ public void setUp() {
+ identityManager = new IdentityManagerImpl(db, crypto, authorFactory);
}
@Test
- public void testRegisterLocalAuthor() throws DbException {
- expectRegisterLocalAuthor();
- identityManager.registerLocalAuthor(localAuthor);
+ public void testCreateLocalAuthor() {
+ context.checking(new Expectations() {{
+ oneOf(crypto).generateSignatureKeyPair();
+ will(returnValue(keyPair));
+ oneOf(publicKey).getEncoded();
+ will(returnValue(publicKeyBytes));
+ oneOf(privateKey).getEncoded();
+ will(returnValue(privateKeyBytes));
+ oneOf(authorFactory).createLocalAuthor(authorName,
+ publicKeyBytes, privateKeyBytes);
+ will(returnValue(localAuthor));
+ }});
+
+ assertEquals(localAuthor,
+ identityManager.createLocalAuthor(authorName));
}
- private void expectRegisterLocalAuthor() throws DbException {
+ @Test
+ public void testRegisterAndStoreLocalAuthor() throws DbException {
context.checking(new Expectations() {{
oneOf(db).startTransaction(false);
will(returnValue(txn));
@@ -52,6 +82,10 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
oneOf(db).commitTransaction(txn);
oneOf(db).endTransaction(txn);
}});
+
+ identityManager.registerLocalAuthor(localAuthor);
+ assertEquals(localAuthor, identityManager.getLocalAuthor());
+ identityManager.storeLocalAuthor();
}
@Test
@@ -69,7 +103,6 @@ public class IdentityManagerImplTest extends BrambleMockTestCase {
@Test
public void testGetCachedLocalAuthor() throws DbException {
- expectRegisterLocalAuthor();
identityManager.registerLocalAuthor(localAuthor);
assertEquals(localAuthor, identityManager.getLocalAuthor());
}
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java
index a830795f3..1c626b483 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java
@@ -1,6 +1,5 @@
package org.briarproject.bramble.test;
-import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -11,7 +10,6 @@ public class TestDatabaseConfig implements DatabaseConfig {
private final File dbDir, keyDir;
private final long maxSize;
- private volatile SecretKey key = new SecretKey(new byte[SecretKey.LENGTH]);
public TestDatabaseConfig(File testDir, long maxSize) {
dbDir = new File(testDir, "db");
@@ -19,13 +17,6 @@ public class TestDatabaseConfig implements DatabaseConfig {
this.maxSize = maxSize;
}
- @Override
- public boolean databaseExists() {
- if (!dbDir.isDirectory()) return false;
- File[] files = dbDir.listFiles();
- return files != null && files.length > 0;
- }
-
@Override
public File getDatabaseDirectory() {
return dbDir;
@@ -36,26 +27,6 @@ public class TestDatabaseConfig implements DatabaseConfig {
return keyDir;
}
- @Override
- public void setEncryptionKey(SecretKey key) {
- this.key = key;
- }
-
- @Override
- public SecretKey getEncryptionKey() {
- return key;
- }
-
- @Override
- public void setLocalAuthorName(String nickname) {
-
- }
-
- @Override
- public String getLocalAuthorName() {
- return null;
- }
-
@Override
public long getMaxSize() {
return maxSize;
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestLifecycleModule.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestLifecycleModule.java
index 15943adc2..21f6ccfe9 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/test/TestLifecycleModule.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestLifecycleModule.java
@@ -1,5 +1,6 @@
package org.briarproject.bramble.test;
+import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.lifecycle.Service;
@@ -11,7 +12,6 @@ import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import javax.annotation.Nullable;
import javax.inject.Singleton;
import dagger.Module;
@@ -40,7 +40,7 @@ public class TestLifecycleModule {
}
@Override
- public StartResult startServices(@Nullable String nickname) {
+ public StartResult startServices(SecretKey dbKey) {
return StartResult.SUCCESS;
}
@@ -49,15 +49,15 @@ public class TestLifecycleModule {
}
@Override
- public void waitForDatabase() throws InterruptedException {
+ public void waitForDatabase() {
}
@Override
- public void waitForStartup() throws InterruptedException {
+ public void waitForStartup() {
}
@Override
- public void waitForShutdown() throws InterruptedException {
+ public void waitForShutdown() {
}
@Override
diff --git a/briar-android/src/main/java/org/briarproject/bramble/account/BriarAccountManager.java b/briar-android/src/main/java/org/briarproject/bramble/account/BriarAccountManager.java
new file mode 100644
index 000000000..eb1e2faaa
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/bramble/account/BriarAccountManager.java
@@ -0,0 +1,33 @@
+package org.briarproject.bramble.account;
+
+import android.app.Application;
+import android.content.SharedPreferences;
+
+import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.briar.R;
+import org.briarproject.briar.android.Localizer;
+import org.briarproject.briar.android.util.UiUtils;
+
+import javax.inject.Inject;
+
+class BriarAccountManager extends AndroidAccountManager {
+
+ @Inject
+ BriarAccountManager(DatabaseConfig databaseConfig, CryptoComponent crypto,
+ IdentityManager identityManager, SharedPreferences prefs,
+ Application app) {
+ super(databaseConfig, crypto, identityManager, prefs, app);
+ }
+
+ @Override
+ public void deleteAccount() {
+ synchronized (stateChangeLock) {
+ super.deleteAccount();
+ Localizer.reinitialize();
+ UiUtils.setTheme(appContext,
+ appContext.getString(R.string.pref_theme_light_value));
+ }
+ }
+}
diff --git a/briar-android/src/main/java/org/briarproject/bramble/account/BriarAccountModule.java b/briar-android/src/main/java/org/briarproject/bramble/account/BriarAccountModule.java
new file mode 100644
index 000000000..3ed782d77
--- /dev/null
+++ b/briar-android/src/main/java/org/briarproject/bramble/account/BriarAccountModule.java
@@ -0,0 +1,18 @@
+package org.briarproject.bramble.account;
+
+import org.briarproject.bramble.api.account.AccountManager;
+
+import javax.inject.Singleton;
+
+import dagger.Module;
+import dagger.Provides;
+
+@Module
+public class BriarAccountModule {
+
+ @Provides
+ @Singleton
+ AccountManager provideAccountManager(BriarAccountManager accountManager) {
+ return accountManager;
+ }
+}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java
index 087aa5ea0..75b25b099 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java
@@ -1,16 +1,14 @@
package org.briarproject.briar.android;
-import android.content.SharedPreferences;
-
import org.briarproject.bramble.BrambleAndroidModule;
import org.briarproject.bramble.BrambleCoreEagerSingletons;
import org.briarproject.bramble.BrambleCoreModule;
+import org.briarproject.bramble.account.BriarAccountModule;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.contact.ContactExchangeTask;
import org.briarproject.bramble.api.contact.ContactManager;
-import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
-import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager;
@@ -62,6 +60,7 @@ import dagger.Component;
BrambleCoreModule.class,
BriarCoreModule.class,
BrambleAndroidModule.class,
+ BriarAccountModule.class,
AppModule.class
})
public interface AndroidComponent
@@ -73,10 +72,6 @@ public interface AndroidComponent
PasswordStrengthEstimator passwordStrengthIndicator();
- CryptoComponent cryptoComponent();
-
- DatabaseConfig databaseConfig();
-
@DatabaseExecutor
Executor databaseExecutor();
@@ -92,8 +87,6 @@ public interface AndroidComponent
AndroidNotificationManager androidNotificationManager();
- SharedPreferences sharedPreferences();
-
ScreenFilterMonitor screenFilterMonitor();
ConnectionRegistry connectionRegistry();
@@ -151,6 +144,8 @@ public interface AndroidComponent
@IoExecutor
Executor ioExecutor();
+ AccountManager accountManager();
+
void inject(SignInReminderReceiver briarService);
void inject(BriarService briarService);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java
index e92a356ee..797fb907c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java
@@ -1,99 +1,30 @@
package org.briarproject.briar.android;
-import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.io.File;
-import java.util.logging.Logger;
-
-import javax.annotation.Nullable;
-
-import static java.util.logging.Level.INFO;
@NotNullByDefault
class AndroidDatabaseConfig implements DatabaseConfig {
- private static final Logger LOG =
- Logger.getLogger(AndroidDatabaseConfig.class.getName());
-
private final File dbDir, keyDir;
- @Nullable
- private volatile SecretKey key = null;
- @Nullable
- private volatile String nickname = null;
-
AndroidDatabaseConfig(File dbDir, File keyDir) {
this.dbDir = dbDir;
this.keyDir = keyDir;
}
- @Override
- public boolean databaseExists() {
- // FIXME should not run on UiThread #620
- if (!dbDir.isDirectory()) {
- if (LOG.isLoggable(INFO))
- LOG.info(dbDir.getAbsolutePath() + " is not a directory");
- return false;
- }
- File[] files = dbDir.listFiles();
- if (LOG.isLoggable(INFO)) {
- if (files == null) {
- LOG.info("Could not list files in " + dbDir.getAbsolutePath());
- } else {
- LOG.info("Files in " + dbDir.getAbsolutePath() + ":");
- for (File f : files) LOG.info(f.getName());
- }
- LOG.info("Database exists: " + (files != null && files.length > 0));
- }
- return files != null && files.length > 0;
- }
-
@Override
public File getDatabaseDirectory() {
- if (LOG.isLoggable(INFO))
- LOG.info("Database directory: " + dbDir.getAbsolutePath());
return dbDir;
}
@Override
public File getDatabaseKeyDirectory() {
- if (LOG.isLoggable(INFO))
- LOG.info("Database key directory: " + keyDir.getAbsolutePath());
return keyDir;
}
- @Override
- public void setEncryptionKey(SecretKey key) {
- LOG.info("Setting database key");
- this.key = key;
- }
-
- @Override
- public void setLocalAuthorName(String nickname) {
- LOG.info("Setting local author name");
- this.nickname = nickname;
- }
-
- @Override
- @Nullable
- public String getLocalAuthorName() {
- String nickname = this.nickname;
- if (LOG.isLoggable(INFO))
- LOG.info("Local author name has been set: " + (nickname != null));
- return nickname;
- }
-
- @Override
- @Nullable
- public SecretKey getEncryptionKey() {
- SecretKey key = this.key;
- if (LOG.isLoggable(INFO))
- LOG.info("Database key has been set: " + (key != null));
- return key;
- }
-
@Override
public long getMaxSize() {
return Long.MAX_VALUE;
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
index 2296a8baf..3af716277 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
@@ -11,9 +11,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
-import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
@@ -86,11 +84,7 @@ public class AppModule {
File dbDir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
File keyDir = app.getApplicationContext().getDir("key", MODE_PRIVATE);
StrictMode.setThreadPolicy(tp);
- @MethodsNotNullByDefault
- @ParametersNotNullByDefault
- DatabaseConfig databaseConfig =
- new AndroidDatabaseConfig(dbDir, keyDir);
- return databaseConfig;
+ return new AndroidDatabaseConfig(dbDir, keyDir);
}
@Provides
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java
index b2a30ecb2..022fa3195 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarService.java
@@ -17,7 +17,8 @@ import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
-import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.account.AccountManager;
+import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult;
import org.briarproject.bramble.api.system.AndroidExecutor;
@@ -74,12 +75,13 @@ public class BriarService extends Service {
private BroadcastReceiver receiver = null;
@Inject
- protected DatabaseConfig databaseConfig;
+ AccountManager accountManager;
+
// Fields that are accessed from background threads must be volatile
@Inject
- protected volatile LifecycleManager lifecycleManager;
+ volatile LifecycleManager lifecycleManager;
@Inject
- protected volatile AndroidExecutor androidExecutor;
+ volatile AndroidExecutor androidExecutor;
private volatile boolean started = false;
@Override
@@ -95,7 +97,8 @@ public class BriarService extends Service {
stopSelf();
return;
}
- if (databaseConfig.getEncryptionKey() == null) {
+ SecretKey dbKey = accountManager.getDatabaseKey();
+ if (dbKey == null) {
LOG.info("No database key");
stopSelf();
return;
@@ -138,8 +141,7 @@ public class BriarService extends Service {
startForeground(ONGOING_NOTIFICATION_ID, b.build());
// Start the services in a background thread
new Thread(() -> {
- String nickname = databaseConfig.getLocalAuthorName();
- StartResult result = lifecycleManager.startServices(nickname);
+ StartResult result = lifecycleManager.startServices(dbKey);
if (result == SUCCESS) {
started = true;
} else if (result == ALREADY_RUNNING) {
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java
index 2651833f1..b21d07232 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java
@@ -4,8 +4,6 @@ import android.app.Activity;
import org.briarproject.briar.android.controller.BriarController;
import org.briarproject.briar.android.controller.BriarControllerImpl;
-import org.briarproject.briar.android.controller.ConfigController;
-import org.briarproject.briar.android.controller.ConfigControllerImpl;
import org.briarproject.briar.android.controller.DbController;
import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.login.PasswordController;
@@ -48,13 +46,6 @@ public class ActivityModule {
return setupController;
}
- @ActivityScope
- @Provides
- ConfigController provideConfigController(
- ConfigControllerImpl configController) {
- return configController;
- }
-
@ActivityScope
@Provides
PasswordController providePasswordController(
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
index 3d2d8361c..cf71839f9 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java
@@ -61,7 +61,7 @@ public abstract class BriarActivity extends BaseActivity {
@Override
public void onStart() {
super.onStart();
- if (!briarController.hasEncryptionKey() && !isFinishing()) {
+ if (!briarController.accountSignedIn() && !isFinishing()) {
Intent i = new Intent(this, PasswordActivity.class);
startActivityForResult(i, REQUEST_PASSWORD);
} else if (SDK_INT >= 23) {
@@ -138,7 +138,7 @@ public abstract class BriarActivity extends BaseActivity {
}
protected void signOut(boolean removeFromRecentApps) {
- if (briarController.hasEncryptionKey()) {
+ if (briarController.accountSignedIn()) {
// Don't use UiResultHandler because we want the result even if
// this activity has been destroyed
briarController.signOut(result -> runOnUiThread(
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarController.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarController.java
index 350faeaba..e4e60c692 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarController.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarController.java
@@ -6,7 +6,7 @@ public interface BriarController extends ActivityLifecycleController {
void startAndBindService();
- boolean hasEncryptionKey();
+ boolean accountSignedIn();
/**
* Returns true via the handler when the app has dozed
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java
index a668a450d..1271f4d21 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java
@@ -5,7 +5,7 @@ import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.CallSuper;
-import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.settings.Settings;
@@ -33,8 +33,7 @@ public class BriarControllerImpl implements BriarController {
public static final String DOZE_ASK_AGAIN = "dozeAskAgain";
private final BriarServiceConnection serviceConnection;
- private final DatabaseConfig databaseConfig;
- @DatabaseExecutor
+ private final AccountManager accountManager;
private final Executor databaseExecutor;
private final SettingsManager settingsManager;
private final DozeWatchdog dozeWatchdog;
@@ -44,12 +43,12 @@ public class BriarControllerImpl implements BriarController {
@Inject
BriarControllerImpl(BriarServiceConnection serviceConnection,
- DatabaseConfig databaseConfig,
+ AccountManager accountManager,
@DatabaseExecutor Executor databaseExecutor,
SettingsManager settingsManager, DozeWatchdog dozeWatchdog,
Activity activity) {
this.serviceConnection = serviceConnection;
- this.databaseConfig = databaseConfig;
+ this.accountManager = accountManager;
this.databaseExecutor = databaseExecutor;
this.settingsManager = settingsManager;
this.dozeWatchdog = dozeWatchdog;
@@ -59,7 +58,7 @@ public class BriarControllerImpl implements BriarController {
@Override
@CallSuper
public void onActivityCreate(Activity activity) {
- if (databaseConfig.getEncryptionKey() != null) startAndBindService();
+ if (accountManager.hasDatabaseKey()) startAndBindService();
}
@Override
@@ -84,8 +83,8 @@ public class BriarControllerImpl implements BriarController {
}
@Override
- public boolean hasEncryptionKey() {
- return databaseConfig.getEncryptionKey() != null;
+ public boolean accountSignedIn() {
+ return accountManager.hasDatabaseKey();
}
@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java
deleted file mode 100644
index e19452a9c..000000000
--- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package org.briarproject.briar.android.controller;
-
-import android.content.Context;
-
-import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-
-import javax.annotation.Nullable;
-
-@NotNullByDefault
-public interface ConfigController {
-
- @Nullable
- String getEncryptedDatabaseKey();
-
- boolean storeEncryptedDatabaseKey(String hex);
-
- void deleteAccount(Context ctx);
-
- boolean accountExists();
-
- boolean accountSignedIn();
-
-}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java
deleted file mode 100644
index 1e0bce3a2..000000000
--- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java
+++ /dev/null
@@ -1,171 +0,0 @@
-package org.briarproject.briar.android.controller;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.support.v7.preference.PreferenceManager;
-
-import org.briarproject.bramble.api.db.DatabaseConfig;
-import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-import org.briarproject.bramble.util.AndroidUtils;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.util.logging.Logger;
-
-import javax.annotation.Nullable;
-import javax.inject.Inject;
-
-import static java.util.logging.Level.WARNING;
-import static org.briarproject.bramble.util.LogUtils.logException;
-
-@NotNullByDefault
-public class ConfigControllerImpl implements ConfigController {
-
- private static final Logger LOG =
- Logger.getLogger(ConfigControllerImpl.class.getName());
-
- private static final String PREF_DB_KEY = "key";
- private static final String DB_KEY_FILENAME = "db.key";
- private static final String DB_KEY_BACKUP_FILENAME = "db.key.bak";
-
- private final SharedPreferences briarPrefs;
- private final File dbKeyFile, dbKeyBackupFile;
- protected final DatabaseConfig databaseConfig;
-
- @Inject
- public ConfigControllerImpl(SharedPreferences briarPrefs,
- DatabaseConfig databaseConfig) {
- this.briarPrefs = briarPrefs;
- this.databaseConfig = databaseConfig;
- File keyDir = databaseConfig.getDatabaseKeyDirectory();
- dbKeyFile = new File(keyDir, DB_KEY_FILENAME);
- dbKeyBackupFile = new File(keyDir, DB_KEY_BACKUP_FILENAME);
- }
-
- @Override
- @Nullable
- public String getEncryptedDatabaseKey() {
- String key = getDatabaseKeyFromPreferences();
- if (key == null) key = getDatabaseKeyFromFile();
- else migrateDatabaseKeyToFile(key);
- return key;
- }
-
- @Nullable
- private String getDatabaseKeyFromPreferences() {
- String key = briarPrefs.getString(PREF_DB_KEY, null);
- if (key == null) LOG.info("No database key in preferences");
- else LOG.info("Found database key in preferences");
- return key;
- }
-
- @Nullable
- private String getDatabaseKeyFromFile() {
- String key = readDbKeyFromFile(dbKeyFile);
- if (key == null) {
- LOG.info("No database key in primary file");
- key = readDbKeyFromFile(dbKeyBackupFile);
- if (key == null) LOG.info("No database key in backup file");
- else LOG.warning("Found database key in backup file");
- } else {
- LOG.info("Found database key in primary file");
- }
- return key;
- }
-
- @Nullable
- private String readDbKeyFromFile(File f) {
- if (!f.exists()) {
- LOG.info("Key file does not exist");
- return null;
- }
- try {
- BufferedReader reader = new BufferedReader(new InputStreamReader(
- new FileInputStream(f), "UTF-8"));
- String key = reader.readLine();
- reader.close();
- return key;
- } catch (IOException e) {
- logException(LOG, WARNING, e);
- return null;
- }
- }
-
- private void migrateDatabaseKeyToFile(String key) {
- if (storeEncryptedDatabaseKey(key)) {
- if (briarPrefs.edit().remove(PREF_DB_KEY).commit())
- LOG.info("Database key migrated to file");
- else LOG.warning("Database key not removed from preferences");
- } else {
- LOG.warning("Database key not migrated to file");
- }
- }
-
- @Override
- public boolean storeEncryptedDatabaseKey(String hex) {
- LOG.info("Storing database key in file");
- // Create the directory if necessary
- if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
- LOG.info("Created database key directory");
- // If only the backup file exists, rename it so we don't overwrite it
- if (dbKeyBackupFile.exists() && !dbKeyFile.exists()) {
- if (dbKeyBackupFile.renameTo(dbKeyFile))
- LOG.info("Renamed old backup");
- else LOG.warning("Failed to rename old backup");
- }
- try {
- // Write to the backup file
- writeDbKeyToFile(hex, dbKeyBackupFile);
- LOG.info("Stored database key in backup file");
- // Delete the old primary file, if it exists
- if (dbKeyFile.exists()) {
- if (dbKeyFile.delete()) LOG.info("Deleted primary file");
- else LOG.warning("Failed to delete primary file");
- }
- // The backup file becomes the new primary
- if (dbKeyBackupFile.renameTo(dbKeyFile)) {
- LOG.info("Renamed backup file to primary");
- } else {
- LOG.warning("Failed to rename backup file to primary");
- return false; // Don't overwrite our only copy
- }
- // Write a second copy to the backup file
- writeDbKeyToFile(hex, dbKeyBackupFile);
- LOG.info("Stored second copy of database key in backup file");
- return true;
- } catch (IOException e) {
- logException(LOG, WARNING, e);
- return false;
- }
- }
-
- private void writeDbKeyToFile(String key, File f) throws IOException {
- FileOutputStream out = new FileOutputStream(f);
- out.write(key.getBytes("UTF-8"));
- out.flush();
- out.close();
- }
-
- @Override
- public void deleteAccount(Context ctx) {
- LOG.info("Deleting account");
- SharedPreferences defaultPrefs =
- PreferenceManager.getDefaultSharedPreferences(ctx);
- AndroidUtils.deleteAppData(ctx, briarPrefs, defaultPrefs);
- }
-
- @Override
- public boolean accountExists() {
- String hex = getEncryptedDatabaseKey();
- return hex != null && databaseConfig.databaseExists();
- }
-
- @Override
- public boolean accountSignedIn() {
- return databaseConfig.getEncryptionKey() != null;
- }
-}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java
index 405801913..79940a314 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java
@@ -13,8 +13,8 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.ProgressBar;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.briar.R;
-import org.briarproject.briar.android.Localizer;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BaseActivity;
import org.briarproject.briar.android.controller.BriarController;
@@ -33,6 +33,9 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.REMI
public class PasswordActivity extends BaseActivity {
+ @Inject
+ AccountManager accountManager;
+
@Inject
PasswordController passwordController;
@@ -50,7 +53,8 @@ public class PasswordActivity extends BaseActivity {
// fade-in after splash screen instead of default animation
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
- if (!passwordController.accountExists()) {
+ if (!accountManager.accountExists()) {
+ // TODO: Finish instead of deleting account?
deleteAccount();
return;
}
@@ -87,7 +91,7 @@ public class PasswordActivity extends BaseActivity {
public void onStart() {
super.onStart();
// If the user has already signed in, clean up this instance
- if (briarController.hasEncryptionKey()) {
+ if (briarController.accountSignedIn()) {
setResult(RESULT_OK);
finish();
} else {
@@ -112,9 +116,7 @@ public class PasswordActivity extends BaseActivity {
}
private void deleteAccount() {
- passwordController.deleteAccount(this);
- Localizer.reinitialize();
- UiUtils.setTheme(this, getString(R.string.pref_theme_light_value));
+ accountManager.deleteAccount();
setResult(RESULT_CANCELED);
Intent i = new Intent(this, SetupActivity.class);
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TASK);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java
index ec3cb7ed7..cef864152 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java
@@ -1,18 +1,17 @@
package org.briarproject.briar.android.login;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-import org.briarproject.briar.android.controller.ConfigController;
import org.briarproject.briar.android.controller.handler.ResultHandler;
@NotNullByDefault
-public interface PasswordController extends ConfigController {
+public interface PasswordController {
float estimatePasswordStrength(String password);
void validatePassword(String password,
ResultHandler resultHandler);
- void changePassword(String password, String newPassword,
+ void changePassword(String oldPassword, String newPassword,
ResultHandler resultHandler);
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
index 905797acc..0ecf78475 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
@@ -1,44 +1,28 @@
package org.briarproject.briar.android.login;
-import android.content.SharedPreferences;
-
-import org.briarproject.bramble.api.crypto.CryptoComponent;
-import org.briarproject.bramble.api.crypto.CryptoExecutor;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
-import org.briarproject.bramble.api.crypto.SecretKey;
-import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-import org.briarproject.bramble.util.StringUtils;
-import org.briarproject.briar.android.controller.ConfigControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultHandler;
import java.util.concurrent.Executor;
-import java.util.logging.Logger;
import javax.inject.Inject;
-import static org.briarproject.bramble.util.LogUtils.logDuration;
-import static org.briarproject.bramble.util.LogUtils.now;
-
@NotNullByDefault
-public class PasswordControllerImpl extends ConfigControllerImpl
- implements PasswordController {
+public class PasswordControllerImpl implements PasswordController {
- private static final Logger LOG =
- Logger.getLogger(PasswordControllerImpl.class.getName());
-
- protected final Executor cryptoExecutor;
- protected final CryptoComponent crypto;
+ protected final AccountManager accountManager;
+ protected final Executor ioExecutor;
private final PasswordStrengthEstimator strengthEstimator;
@Inject
- PasswordControllerImpl(SharedPreferences briarPrefs,
- DatabaseConfig databaseConfig,
- @CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto,
+ PasswordControllerImpl(AccountManager accountManager,
+ @IoExecutor Executor ioExecutor,
PasswordStrengthEstimator strengthEstimator) {
- super(briarPrefs, databaseConfig);
- this.cryptoExecutor = cryptoExecutor;
- this.crypto = crypto;
+ this.accountManager = accountManager;
+ this.ioExecutor = ioExecutor;
this.strengthEstimator = strengthEstimator;
}
@@ -50,46 +34,17 @@ public class PasswordControllerImpl extends ConfigControllerImpl
@Override
public void validatePassword(String password,
ResultHandler resultHandler) {
- byte[] encrypted = getEncryptedKey();
- cryptoExecutor.execute(() -> {
- byte[] key = crypto.decryptWithPassword(encrypted, password);
- if (key == null) {
- resultHandler.onResult(false);
- } else {
- databaseConfig.setEncryptionKey(new SecretKey(key));
- resultHandler.onResult(true);
- }
- });
+ ioExecutor.execute(() ->
+ resultHandler.onResult(accountManager.signIn(password)));
}
@Override
- public void changePassword(String password, String newPassword,
+ public void changePassword(String oldPassword, String newPassword,
ResultHandler resultHandler) {
- byte[] encrypted = getEncryptedKey();
- cryptoExecutor.execute(() -> {
- byte[] key = crypto.decryptWithPassword(encrypted, password);
- if (key == null) {
- resultHandler.onResult(false);
- } else {
- String hex =
- encryptDatabaseKey(new SecretKey(key), newPassword);
- resultHandler.onResult(storeEncryptedDatabaseKey(hex));
- }
+ ioExecutor.execute(() -> {
+ boolean changed =
+ accountManager.changePassword(oldPassword, newPassword);
+ resultHandler.onResult(changed);
});
}
-
- private byte[] getEncryptedKey() {
- String hex = getEncryptedDatabaseKey();
- if (hex == null)
- throw new IllegalStateException("Encrypted database key is null");
- return StringUtils.fromHexString(hex);
- }
-
- @CryptoExecutor
- String encryptDatabaseKey(SecretKey key, String password) {
- long start = now();
- byte[] encrypted = crypto.encryptWithPassword(key.getBytes(), password);
- logDuration(LOG, "Key derivation", start);
- return StringUtils.toHexString(encrypted);
- }
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java
index f91c57512..e38cfeb0c 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java
@@ -4,6 +4,7 @@ import android.annotation.TargetApi;
import android.content.Intent;
import android.os.Bundle;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
@@ -25,6 +26,9 @@ public class SetupActivity extends BaseActivity
private static final String STATE_KEY_AUTHOR_NAME = "authorName";
private static final String STATE_KEY_PASSWORD = "password";
+ @Inject
+ AccountManager accountManager;
+
@Inject
SetupController setupController;
@@ -39,8 +43,7 @@ public class SetupActivity extends BaseActivity
setContentView(R.layout.activity_fragment_container);
if (state == null) {
- if (setupController.accountExists())
- throw new AssertionError();
+ if (accountManager.accountExists()) throw new AssertionError();
showInitialFragment(AuthorNameFragment.newInstance());
} else {
authorName = state.getString(STATE_KEY_AUTHOR_NAME);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java
index 3923cce4a..02f40226e 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java
@@ -1,13 +1,10 @@
package org.briarproject.briar.android.login;
-import android.content.SharedPreferences;
import android.support.annotation.Nullable;
-import org.briarproject.bramble.api.crypto.CryptoComponent;
-import org.briarproject.bramble.api.crypto.CryptoExecutor;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
-import org.briarproject.bramble.api.crypto.SecretKey;
-import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.controller.handler.ResultHandler;
import org.briarproject.briar.android.controller.handler.UiResultHandler;
@@ -28,12 +25,10 @@ public class SetupControllerImpl extends PasswordControllerImpl
private volatile SetupActivity setupActivity;
@Inject
- SetupControllerImpl(SharedPreferences briarPrefs,
- DatabaseConfig databaseConfig,
- @CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto,
+ SetupControllerImpl(AccountManager accountManager,
+ @IoExecutor Executor ioExecutor,
PasswordStrengthEstimator strengthEstimator) {
- super(briarPrefs, databaseConfig, cryptoExecutor, crypto,
- strengthEstimator);
+ super(accountManager, ioExecutor, strengthEstimator);
}
@Override
@@ -80,10 +75,11 @@ public class SetupControllerImpl extends PasswordControllerImpl
@Override
public void createAccount() {
SetupActivity setupActivity = this.setupActivity;
- UiResultHandler resultHandler =
- new UiResultHandler(setupActivity) {
+ UiResultHandler resultHandler =
+ new UiResultHandler(setupActivity) {
@Override
- public void onResultUi(Void result) {
+ public void onResultUi(Boolean result) {
+ // TODO: Show an error if result is false
if (setupActivity == null)
throw new IllegalStateException();
setupActivity.showApp();
@@ -93,22 +89,17 @@ public class SetupControllerImpl extends PasswordControllerImpl
}
// Package access for testing
- void createAccount(ResultHandler resultHandler) {
+ void createAccount(ResultHandler resultHandler) {
SetupActivity setupActivity = this.setupActivity;
if (setupActivity == null) throw new IllegalStateException();
String authorName = setupActivity.getAuthorName();
if (authorName == null) throw new IllegalStateException();
String password = setupActivity.getPassword();
if (password == null) throw new IllegalStateException();
- cryptoExecutor.execute(() -> {
+ ioExecutor.execute(() -> {
LOG.info("Creating account");
- databaseConfig.setLocalAuthorName(authorName);
- SecretKey key = crypto.generateSecretKey();
- databaseConfig.setEncryptionKey(key);
- String hex = encryptDatabaseKey(key, password);
- storeEncryptedDatabaseKey(hex);
- resultHandler.onResult(null);
+ resultHandler.onResult(accountManager.createAccount(authorName,
+ password));
});
}
-
}
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SignInReminderReceiver.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SignInReminderReceiver.java
index cf1edaae6..0ed79eddf 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/SignInReminderReceiver.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/SignInReminderReceiver.java
@@ -10,7 +10,7 @@ import android.content.SharedPreferences;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
-import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.briar.R;
import org.briarproject.briar.android.AndroidComponent;
import org.briarproject.briar.android.BriarApplication;
@@ -37,7 +37,7 @@ public class SignInReminderReceiver extends BroadcastReceiver {
public static final String DISMISS_REMINDER = "dismissReminder";
@Inject
- DatabaseConfig databaseConfig;
+ AccountManager accountManager;
@Override
public void onReceive(Context ctx, Intent intent) {
@@ -51,7 +51,7 @@ public class SignInReminderReceiver extends BroadcastReceiver {
if (action == null) return;
if (action.equals(ACTION_BOOT_COMPLETED) ||
action.equals(ACTION_MY_PACKAGE_REPLACED)) {
- if (databaseConfig.databaseExists()) {
+ if (accountManager.accountExists()) {
SharedPreferences prefs = app.getDefaultSharedPreferences();
if (prefs.getBoolean(NOTIFY_SIGN_IN, true)) {
showSignInNotification(ctx);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/panic/PanicResponderActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/panic/PanicResponderActivity.java
index dd122e149..9d64d1500 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/panic/PanicResponderActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/panic/PanicResponderActivity.java
@@ -7,10 +7,10 @@ import android.os.Build;
import android.os.Bundle;
import android.support.v7.preference.PreferenceManager;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
-import org.briarproject.briar.android.controller.ConfigController;
import org.iilab.IilabEngineeringRSA2048Pin;
import java.util.logging.Logger;
@@ -33,7 +33,7 @@ public class PanicResponderActivity extends BriarActivity {
Logger.getLogger(PanicResponderActivity.class.getName());
@Inject
- protected ConfigController configController;
+ protected AccountManager accountManager;
@Inject
protected AndroidExecutor androidExecutor;
@@ -94,7 +94,7 @@ public class PanicResponderActivity extends BriarActivity {
private void deleteAllData() {
androidExecutor.runOnBackgroundThread(() -> {
- configController.deleteAccount(PanicResponderActivity.this);
+ accountManager.deleteAccount();
// TODO somehow delete/shred the database more thoroughly
PanicResponder.deleteAllAppData(PanicResponderActivity.this);
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java
index d207508a4..e878d2376 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java
@@ -7,11 +7,11 @@ import android.os.Handler;
import android.support.v7.preference.PreferenceManager;
import android.transition.Fade;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BaseActivity;
-import org.briarproject.briar.android.controller.ConfigController;
import org.briarproject.briar.android.login.OpenDatabaseActivity;
import org.briarproject.briar.android.login.SetupActivity;
@@ -27,7 +27,7 @@ public class SplashScreenActivity extends BaseActivity {
Logger.getLogger(SplashScreenActivity.class.getName());
@Inject
- protected ConfigController configController;
+ protected AccountManager accountManager;
@Inject
protected AndroidExecutor androidExecutor;
@@ -43,7 +43,7 @@ public class SplashScreenActivity extends BaseActivity {
setContentView(R.layout.splash);
- if (configController.accountSignedIn()) {
+ if (accountManager.hasDatabaseKey()) {
startActivity(new Intent(this, OpenDatabaseActivity.class));
finish();
} else {
@@ -64,12 +64,12 @@ public class SplashScreenActivity extends BaseActivity {
LOG.info("Expired");
startActivity(new Intent(this, ExpiredActivity.class));
} else {
- if (configController.accountExists()) {
+ if (accountManager.accountExists()) {
LOG.info("Account exists");
startActivity(new Intent(this, OpenDatabaseActivity.class));
} else {
LOG.info("Account does not exist");
- configController.deleteAccount(this);
+ accountManager.deleteAccount();
startActivity(new Intent(this, SetupActivity.class));
}
}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/TestDatabaseKeyUtils.java b/briar-android/src/test/java/org/briarproject/briar/android/TestDatabaseKeyUtils.java
deleted file mode 100644
index 0e9173105..000000000
--- a/briar-android/src/test/java/org/briarproject/briar/android/TestDatabaseKeyUtils.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.briarproject.briar.android;
-
-import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-
-import javax.annotation.Nullable;
-
-import static junit.framework.Assert.assertTrue;
-
-@NotNullByDefault
-public class TestDatabaseKeyUtils {
-
- public static void storeDatabaseKey(File f, String hex) throws IOException {
- f.getParentFile().mkdirs();
- FileOutputStream out = new FileOutputStream(f);
- out.write(hex.getBytes("UTF-8"));
- out.flush();
- out.close();
- }
-
- @Nullable
- public static String loadDatabaseKey(File f) throws IOException {
- BufferedReader reader = new BufferedReader(new InputStreamReader(
- new FileInputStream(f), "UTF-8"));
- String hex = reader.readLine();
- reader.close();
- return hex;
- }
-}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java
deleted file mode 100644
index 859792b3c..000000000
--- a/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java
+++ /dev/null
@@ -1,205 +0,0 @@
-package org.briarproject.briar.android.controller;
-
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-
-import org.briarproject.bramble.api.db.DatabaseConfig;
-import org.briarproject.bramble.test.BrambleMockTestCase;
-import org.jmock.Expectations;
-import org.junit.After;
-import org.junit.Test;
-
-import java.io.File;
-
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
-import static junit.framework.Assert.assertNull;
-import static junit.framework.Assert.assertTrue;
-import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
-import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
-import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
-import static org.briarproject.bramble.util.StringUtils.toHexString;
-import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
-import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey;
-
-public class ConfigControllerImplTest extends BrambleMockTestCase {
-
- private final SharedPreferences prefs =
- context.mock(SharedPreferences.class);
- private final DatabaseConfig databaseConfig =
- context.mock(DatabaseConfig.class);
- private final Editor editor = context.mock(Editor.class);
-
- private final byte[] encryptedKey = getRandomBytes(123);
- private final String encryptedKeyHex = toHexString(encryptedKey);
- private final String oldEncryptedKeyHex = toHexString(getRandomBytes(123));
- private final File testDir = getTestDirectory();
- private final File keyDir = new File(testDir, "key");
- private final File keyFile = new File(keyDir, "db.key");
- private final File keyBackupFile = new File(keyDir, "db.key.bak");
-
- @Test
- public void testDbKeyIsMigratedFromPreferencesToFile() throws Exception {
- context.checking(new Expectations() {{
- oneOf(prefs).getString("key", null);
- will(returnValue(encryptedKeyHex));
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
- oneOf(prefs).edit();
- will(returnValue(editor));
- oneOf(editor).remove("key");
- will(returnValue(editor));
- oneOf(editor).commit();
- will(returnValue(true));
- }});
-
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- ConfigControllerImpl c = new ConfigControllerImpl(prefs,
- databaseConfig);
-
- assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
-
- assertTrue(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
- }
-
- @Test
- public void testDbKeyIsLoadedFromPrimaryFile() throws Exception {
- context.checking(new Expectations() {{
- oneOf(prefs).getString("key", null);
- will(returnValue(null));
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
- }});
-
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- storeDatabaseKey(keyFile, encryptedKeyHex);
-
- assertTrue(keyFile.exists());
- assertFalse(keyBackupFile.exists());
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
-
- ConfigControllerImpl c = new ConfigControllerImpl(prefs,
- databaseConfig);
-
- assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
-
- assertTrue(keyFile.exists());
- assertFalse(keyBackupFile.exists());
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
- }
-
- @Test
- public void testDbKeyIsLoadedFromBackupFile() throws Exception {
- context.checking(new Expectations() {{
- oneOf(prefs).getString("key", null);
- will(returnValue(null));
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
- }});
-
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- storeDatabaseKey(keyBackupFile, encryptedKeyHex);
-
- assertFalse(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
-
- ConfigControllerImpl c = new ConfigControllerImpl(prefs,
- databaseConfig);
-
- assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
-
- assertFalse(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
- }
-
- @Test
- public void testDbKeyIsNullIfNotFound() {
- context.checking(new Expectations() {{
- oneOf(prefs).getString("key", null);
- will(returnValue(null));
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
- }});
-
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- ConfigControllerImpl c = new ConfigControllerImpl(prefs,
- databaseConfig);
-
- assertNull(c.getEncryptedDatabaseKey());
-
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
- }
-
- @Test
- public void testStoringDbKeyOverwritesPrimary() throws Exception {
- context.checking(new Expectations() {{
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
- }});
-
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- storeDatabaseKey(keyFile, oldEncryptedKeyHex);
-
- assertTrue(keyFile.exists());
- assertFalse(keyBackupFile.exists());
- assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyFile));
-
- ConfigController c = new ConfigControllerImpl(prefs,
- databaseConfig);
-
- assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex));
-
- assertTrue(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
- }
-
- @Test
- public void testStoringDbKeyOverwritesBackup() throws Exception {
- context.checking(new Expectations() {{
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
- }});
-
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- storeDatabaseKey(keyBackupFile, oldEncryptedKeyHex);
-
- assertFalse(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
-
- ConfigController c = new ConfigControllerImpl(prefs,
- databaseConfig);
-
- assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex));
-
- assertTrue(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
- assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
- }
-
- @After
- public void tearDown() {
- deleteTestDirectory(testDir);
- }
-}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/forum/TestForumActivity.java b/briar-android/src/test/java/org/briarproject/briar/android/forum/TestForumActivity.java
index 72b8d6644..7a04b4517 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/forum/TestForumActivity.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/forum/TestForumActivity.java
@@ -45,7 +45,7 @@ public class TestForumActivity extends ForumActivity {
protected BriarController provideBriarController(
BriarControllerImpl briarController) {
BriarController c = Mockito.mock(BriarController.class);
- Mockito.when(c.hasEncryptionKey()).thenReturn(true);
+ Mockito.when(c.accountSignedIn()).thenReturn(true);
return c;
}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java
index 427dfa772..987155ffc 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java
@@ -1,124 +1,58 @@
package org.briarproject.briar.android.login;
-import android.content.SharedPreferences;
-
-import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
-import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.jmock.Expectations;
-import org.junit.After;
import org.junit.Test;
-import java.io.File;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
-import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
-import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
-import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
-import static org.briarproject.bramble.test.TestUtils.getSecretKey;
-import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
-import static org.briarproject.bramble.util.StringUtils.toHexString;
-import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
-import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey;
+import static org.briarproject.bramble.util.StringUtils.getRandomString;
public class PasswordControllerImplTest extends BrambleMockTestCase {
- private final SharedPreferences briarPrefs =
- context.mock(SharedPreferences.class);
- private final DatabaseConfig databaseConfig =
- context.mock(DatabaseConfig.class);
- private final CryptoComponent crypto = context.mock(CryptoComponent.class);
+ private final AccountManager accountManager =
+ context.mock(AccountManager.class);
private final PasswordStrengthEstimator estimator =
context.mock(PasswordStrengthEstimator.class);
- private final Executor cryptoExecutor = new ImmediateExecutor();
+ private final Executor ioExecutor = new ImmediateExecutor();
- private final String oldPassword = "some.old.pass";
- private final String newPassword = "some.new.pass";
- private final byte[] oldEncryptedKey = getRandomBytes(123);
- private final byte[] newEncryptedKey = getRandomBytes(123);
- private final byte[] key = getSecretKey().getBytes();
- private final File testDir = getTestDirectory();
- private final File keyDir = new File(testDir, "key");
- private final File keyFile = new File(keyDir, "db.key");
- private final File keyBackupFile = new File(keyDir, "db.key.bak");
+ private final String oldPassword = getRandomString(10);
+ private final String newPassword = getRandomString(10);
@Test
- public void testChangePasswordReturnsTrue() throws Exception {
+ public void testChangePasswordReturnsTrue() {
context.checking(new Expectations() {{
- // Look up the encrypted DB key
- oneOf(briarPrefs).getString("key", null);
- will(returnValue(null));
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
- // Decrypt and re-encrypt the key
- oneOf(crypto).decryptWithPassword(oldEncryptedKey, oldPassword);
- will(returnValue(key));
- oneOf(crypto).encryptWithPassword(key, newPassword);
- will(returnValue(newEncryptedKey));
+ oneOf(accountManager).changePassword(oldPassword, newPassword);
+ will(returnValue(true));
}});
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- storeDatabaseKey(keyFile, toHexString(oldEncryptedKey));
- storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey));
-
- PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,
- databaseConfig, cryptoExecutor, crypto, estimator);
+ PasswordControllerImpl p = new PasswordControllerImpl(accountManager,
+ ioExecutor, estimator);
AtomicBoolean capturedResult = new AtomicBoolean(false);
p.changePassword(oldPassword, newPassword, capturedResult::set);
assertTrue(capturedResult.get());
-
- assertTrue(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(toHexString(newEncryptedKey), loadDatabaseKey(keyFile));
- assertEquals(toHexString(newEncryptedKey),
- loadDatabaseKey(keyBackupFile));
}
@Test
- public void testChangePasswordReturnsFalseIfOldPasswordIsWrong()
- throws Exception {
+ public void testChangePasswordReturnsFalseIfOldPasswordIsWrong() {
context.checking(new Expectations() {{
- // Look up the encrypted DB key
- oneOf(briarPrefs).getString("key", null);
- will(returnValue(null));
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
- // Try to decrypt the key - the password is wrong
- oneOf(crypto).decryptWithPassword(oldEncryptedKey, oldPassword);
- will(returnValue(null));
+ oneOf(accountManager).changePassword(oldPassword, newPassword);
+ will(returnValue(false));
}});
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- storeDatabaseKey(keyFile, toHexString(oldEncryptedKey));
- storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey));
-
- PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,
- databaseConfig, cryptoExecutor, crypto, estimator);
+ PasswordControllerImpl p = new PasswordControllerImpl(accountManager,
+ ioExecutor, estimator);
AtomicBoolean capturedResult = new AtomicBoolean(true);
p.changePassword(oldPassword, newPassword, capturedResult::set);
assertFalse(capturedResult.get());
-
- assertTrue(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(toHexString(oldEncryptedKey), loadDatabaseKey(keyFile));
- assertEquals(toHexString(oldEncryptedKey),
- loadDatabaseKey(keyBackupFile));
- }
-
- @After
- public void tearDown() {
- deleteTestDirectory(testDir);
}
}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java
index b0df4cde8..2ee6d0594 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java
@@ -1,56 +1,32 @@
package org.briarproject.briar.android.login;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-
-import org.briarproject.bramble.api.crypto.CryptoComponent;
+import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
-import org.briarproject.bramble.api.crypto.SecretKey;
-import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor;
import org.jmock.Expectations;
import org.jmock.lib.legacy.ClassImposteriser;
-import org.junit.After;
import org.junit.Test;
-import java.io.File;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
-import static junit.framework.Assert.assertEquals;
-import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
-import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
-import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
-import static org.briarproject.bramble.test.TestUtils.getSecretKey;
-import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.util.StringUtils.getRandomString;
-import static org.briarproject.bramble.util.StringUtils.toHexString;
-import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
public class SetupControllerImplTest extends BrambleMockTestCase {
- private final SharedPreferences briarPrefs =
- context.mock(SharedPreferences.class);
- private final DatabaseConfig databaseConfig =
- context.mock(DatabaseConfig.class);
- private final CryptoComponent crypto = context.mock(CryptoComponent.class);
+ private final AccountManager accountManager =
+ context.mock(AccountManager.class);
private final PasswordStrengthEstimator estimator =
context.mock(PasswordStrengthEstimator.class);
private final SetupActivity setupActivity;
- private final Executor cryptoExecutor = new ImmediateExecutor();
+ private final Executor ioExecutor = new ImmediateExecutor();
private final String authorName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
- private final String password = "some.strong.pass";
- private final byte[] encryptedKey = getRandomBytes(123);
- private final SecretKey key = getSecretKey();
- private final File testDir = getTestDirectory();
- private final File keyDir = new File(testDir, "key");
- private final File keyFile = new File(keyDir, "db.key");
- private final File keyBackupFile = new File(keyDir, "db.key.bak");
+ private final String password = getRandomString(10);
public SetupControllerImplTest() {
context.setImposteriser(ClassImposteriser.INSTANCE);
@@ -59,13 +35,8 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
@Test
@SuppressWarnings("ResultOfMethodCallIgnored")
- public void testCreateAccount() throws Exception {
+ public void testCreateAccount() {
context.checking(new Expectations() {{
- // Allow the contents of the data directory to be logged
- allowing(setupActivity).getApplicationInfo();
- will(returnValue(new ApplicationInfo() {{
- dataDir = testDir.getAbsolutePath();
- }}));
// Set the author name and password
oneOf(setupActivity).setAuthorName(authorName);
oneOf(setupActivity).setPassword(password);
@@ -74,25 +45,13 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
will(returnValue(authorName));
oneOf(setupActivity).getPassword();
will(returnValue(password));
- // Generate a database key
- oneOf(crypto).generateSecretKey();
- will(returnValue(key));
- // Attach the author name and database key to the database config
- oneOf(databaseConfig).setLocalAuthorName(authorName);
- oneOf(databaseConfig).setEncryptionKey(key);
- // Encrypt the key with the password
- oneOf(crypto).encryptWithPassword(key.getBytes(), password);
- will(returnValue(encryptedKey));
- // Store the encrypted key
- allowing(databaseConfig).getDatabaseKeyDirectory();
- will(returnValue(keyDir));
+ // Create the account
+ oneOf(accountManager).createAccount(authorName, password);
+ will(returnValue(true));
}});
- assertFalse(keyFile.exists());
- assertFalse(keyBackupFile.exists());
-
- SetupControllerImpl s = new SetupControllerImpl(briarPrefs,
- databaseConfig, cryptoExecutor, crypto, estimator);
+ SetupControllerImpl s = new SetupControllerImpl(accountManager,
+ ioExecutor, estimator);
s.setSetupActivity(setupActivity);
AtomicBoolean called = new AtomicBoolean(false);
@@ -100,15 +59,5 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
s.setPassword(password);
s.createAccount(result -> called.set(true));
assertTrue(called.get());
-
- assertTrue(keyFile.exists());
- assertTrue(keyBackupFile.exists());
- assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyFile));
- assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyBackupFile));
- }
-
- @After
- public void tearDown() {
- deleteTestDirectory(testDir);
}
}
diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java
index 24c7334e7..ced26a4a1 100644
--- a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTest.java
@@ -1,5 +1,7 @@
package org.briarproject.briar.feed;
+import org.briarproject.bramble.api.identity.IdentityManager;
+import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoExecutorModule;
@@ -25,6 +27,7 @@ import org.junit.Test;
import java.io.File;
import java.util.Collection;
+import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -47,8 +50,12 @@ public class FeedManagerIntegrationTest extends BriarTestCase {
component.inject(this);
injectEagerSingletons(component);
+ IdentityManager identityManager = component.getIdentityManager();
+ LocalAuthor localAuthor = identityManager.createLocalAuthor("feedTest");
+ identityManager.registerLocalAuthor(localAuthor);
+
lifecycleManager = component.getLifecycleManager();
- lifecycleManager.startServices("feedTest");
+ lifecycleManager.startServices(getSecretKey());
lifecycleManager.waitForStartup();
feedManager = component.getFeedManager();
diff --git a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java
index 76445fe03..685eedacb 100644
--- a/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java
+++ b/briar-core/src/test/java/org/briarproject/briar/feed/FeedManagerIntegrationTestComponent.java
@@ -1,5 +1,6 @@
package org.briarproject.briar.feed;
+import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.contact.ContactModule;
@@ -76,6 +77,8 @@ interface FeedManagerIntegrationTestComponent {
void inject(VersioningModule.EagerSingletons init);
+ IdentityManager getIdentityManager();
+
LifecycleManager getLifecycleManager();
FeedManager getFeedManager();
diff --git a/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java
index 1007afe5e..c17f3a35d 100644
--- a/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/messaging/SimplexMessagingIntegrationTest.java
@@ -44,7 +44,6 @@ import java.io.InputStream;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestPluginConfigModule.MAX_LATENCY;
import static org.briarproject.bramble.test.TestPluginConfigModule.TRANSPORT_ID;
-import static org.briarproject.bramble.test.TestUtils.getLocalAuthor;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.junit.Assert.assertEquals;
@@ -58,8 +57,6 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
private final File bobDir = new File(testDir, "bob");
private final SecretKey master = getSecretKey();
private final long timestamp = System.currentTimeMillis();
- private final LocalAuthor aliceAuthor = getLocalAuthor();
- private final LocalAuthor bobAuthor = getLocalAuthor();
private SimplexMessagingIntegrationTestComponent alice, bob;
@@ -76,6 +73,11 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
@Test
public void testWriteAndRead() throws Exception {
+ // Create the identities
+ LocalAuthor aliceAuthor =
+ alice.getIdentityManager().createLocalAuthor("Alice");
+ LocalAuthor bobAuthor =
+ bob.getIdentityManager().createLocalAuthor("Bob");
// Set up the devices and get the contact IDs
ContactId bobId = setUp(alice, aliceAuthor, bobAuthor, true);
ContactId aliceId = setUp(bob, bobAuthor, aliceAuthor, false);
@@ -98,13 +100,13 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
private ContactId setUp(SimplexMessagingIntegrationTestComponent device,
LocalAuthor local, Author remote, boolean alice) throws Exception {
- // Start the lifecycle manager
- LifecycleManager lifecycleManager = device.getLifecycleManager();
- lifecycleManager.startServices(null);
- lifecycleManager.waitForStartup();
// Add an identity for the user
IdentityManager identityManager = device.getIdentityManager();
identityManager.registerLocalAuthor(local);
+ // Start the lifecycle manager
+ LifecycleManager lifecycleManager = device.getLifecycleManager();
+ lifecycleManager.startServices(getSecretKey());
+ lifecycleManager.waitForStartup();
// Add the other user as a contact
ContactManager contactManager = device.getContactManager();
return contactManager.addContact(remote, local.getId(), master,
diff --git a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTest.java b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTest.java
index 481a57486..f10d94183 100644
--- a/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTest.java
+++ b/briar-core/src/test/java/org/briarproject/briar/test/BriarIntegrationTest.java
@@ -160,8 +160,8 @@ public abstract class BriarIntegrationTest