diff --git a/.idea/runConfigurations/All_tests.xml b/.idea/runConfigurations/All_tests.xml index 1882b817b1c49ce97019e5a1b6499a1d154e88c2..71857d973c843a84de15b6a8e9addfed60b743b4 100644 --- a/.idea/runConfigurations/All_tests.xml +++ b/.idea/runConfigurations/All_tests.xml @@ -21,6 +21,7 @@ <method> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-api" run_configuration_type="AndroidJUnit" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-core" run_configuration_type="AndroidJUnit" /> + <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-android" run_configuration_type="AndroidJUnit" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in bramble-j2se" run_configuration_type="AndroidJUnit" /> <option name="RunConfigurationTask" enabled="true" run_configuration_name="All tests in briar-core" run_configuration_type="AndroidJUnit" /> </method> diff --git a/bramble-android/build.gradle b/bramble-android/build.gradle index b41f1cebe01613e8760ce235adf65e0681e75af9..f7c9415ed49dc42d6e0c25af540a773d02e9f47e 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 0000000000000000000000000000000000000000..f23e249ae0031865ef58dea3ecd7a7026e84dc89 --- /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 c1acfff7ee9d4219a1b2391dabfea584e25cb091..3625e206e1237f6ca880a45804151bd5b90319f4 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 6a9999b9cbf99da81290a512d57087a4eebc8bd8..bf194fa4e12d9a399a00f29e127986f062a6e314 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 9f9dd972d078b36d284c8b1fbfd7c1bfe56f8e15..5c0d073bd2da5b182026b7c3e069420a736dbda8 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 0000000000000000000000000000000000000000..6736533a69fee377ddd180789dec515816a1f5c0 --- /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 0000000000000000000000000000000000000000..2e1b5a9510066907b55c83621c613fe8c2616366 --- /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. + * <p/> + * 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 1f9daf48afc077a71ca87fa1a0a00de4db46cd99..517dcb89f3647d9d95b9c90fe3acc2461b442407 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 914b88a2e2d1db1a8bafa328850ca07dd80ed189..f4161cee44ef19c37a4604c5ad6c24a585e36f09 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<MessageId> 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 0be85eba93a8ab16a915e5574cc4b0329ea8112b..f096f1fec8e99185d0679846e536613525e38ec0 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 bb584fec45dc2f9734aff102718ef412577dafa4..8160eb06d3bd5dc93ee8babad28436962a50b078 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 d3cd1bc245f33b855d522f47b32f0501689668b7..c44cf8879e4bc117f14ad244155f2142023d14af 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 1718cac8101fc0b9a03719219b5bdaad23f72d84..3b5c90342e73968df59c660f49fe52e55c39abe1 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 0cb2fc478d34206fa5b3fa3420d2c0486b747a42..00b3d672cf85aac26b559df0d92f412c27ff9445 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 0000000000000000000000000000000000000000..7eda4688be9d8e0cdf87abf0dcbb1dcc99a83124 --- /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 0000000000000000000000000000000000000000..e8220176a8ab5863328988945125138ff89c2124 --- /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 66e5cfcf6d72ec90eed0dffa4bbf56c51d38b071..796e4bbe40720e23a9e6ab98b8e5559fb4c4cd8d 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<T> { * @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<T> { * 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 f2e4de1c6fff7cbd3fbc6e92f52dead50c34b458..2913c783f4407344a04ab9e63d8b87bafbd2863f 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<T> 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 1c1983e6771a2cc174ebcbf68ede5879aab7569f..f400d1ce984c7c485fb50a8828fb3c273fa482aa 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 6a87ededae7b862c992745f15ac5695f881a6cf0..447537d8c8098a730e3c7ea217f1e7f001dd5c63 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 baa67a29ac6397c6c7e26b17a18feb455924c111..fd248f811e9b8c703ead29088b49317409792785 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<Connection> { 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 11e8220c9c5a72206604e24968eaeeaa388807af..44bf94550620c9bfcf3ce9a2772d4e511c4d6857 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 68c694189b663a9fda79f5614f428ef876e74640..130595ab63b3ac235af747a4a4ec8662cc07733e 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<Service> services; private final List<Client> clients; private final List<ExecutorService> 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 8fcf789ed91d725b1dbb14ae2493324f785ca7d6..43db266efb188d5b3a3c2bbe90d48e194f7b1990 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 0000000000000000000000000000000000000000..84c5da72f4a70d79bb8c45254d4436fc6ea0afc8 --- /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 e16e03da0dc51086f73c9c6ed45ee81483f8a3b1..676cac2e3708b0465afe2bc08ccaf3ff7a308988 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 89bdf8a4c1a8da94963bc41c7473be59b3f7c063..0740bdfc2301bff10330aba7dc99114936f0ace3 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<Connection> createDatabase( @@ -62,7 +65,7 @@ public abstract class DatabaseMigrationTest extends BrambleMockTestCase { public void testDoesNotRunMigrationsWhenCreatingDatabase() throws Exception { Database<Connection> 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<Connection> 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<Connection> 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<Connection> 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<Connection> 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<Connection> 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<Connection> 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<Connection> 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 1807d30dc33374654a740b4acb58336bee1f5b52..279d1b56e6e0ae44fdb1929a37154a7af6c854e9 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<Connection> 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 f4e0daa19dedf3d6e0187a4aa1b7e8d181136906..2060ca71ec35807190e04518fe123e257fbc6a59 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<Connection> openDatabase() throws DbException { Database<Connection> 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 6a868fd42fa9ff2098087fbf9aa5b5c9f3d346c9..29bdbfa5f5b962a427ffdeeef8f9f94429be8ca4 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<Connection> createDatabase(List<Migration<Connection>> migrations) - throws Exception { + Database<Connection> createDatabase( + List<Migration<Connection>> migrations) { return new H2Database(config, clock) { @Override List<Migration<Connection>> 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 2655e9ef7f571cdbcc80abb2a492e3b0e7c5bdcd..e88038258ad7007b909eb499cc5243eaee11d398 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<Connection> 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 670c7cddb79746be691317387a0c779d0c6b5213..6256d39cd07c51d5ee2bba18983fdecef20f41c1 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<Connection> openDatabase() throws DbException { Database<Connection> 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 e24499ba7f8be40b8a8e685510b4582bf19bdd3a..2926cd3f3edb00a88e05e0fa78bbe04c875b70b0 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<LocalAuthor> localAuthors = Collections.singletonList(localAuthor); - - public IdentityManagerImplTest() { - identityManager = new IdentityManagerImpl(db); + 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; + + @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 a830795f3dd4d28fecb7876df4965d543de1fb5c..1c626b483beb0fe426c8e7e9268f264b353cead9 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 15943adc2b6dabfa86fc8ebb72d270ad396a00ed..21f6ccfe9f8e1020542c806c746387c2f29b8db6 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 0000000000000000000000000000000000000000..eb1e2faaae939632ddbca1d6e1a1284f9a8eff94 --- /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 0000000000000000000000000000000000000000..3ed782d77ac791db1b68f0fcf1504e195de57ddd --- /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 087aa5ea0cd7d6a2f6b76eb9c9b3a82d92e269fc..75b25b0996b4af139b5c46e7b6d41e54a57f151b 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 e92a356ee42f675fb8777cd1c4e361629d82367f..797fb907c129a904c3ee173774d7285cb3fd380c 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 2296a8bafffb48d0e8c315e3460846b9b3d3bef8..3af71627783a7632a3f2c848456f200207a7b318 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 b2a30ecb25bafc0b65d5e42fa1a58e8c4d57cafe..022fa3195154fbe83d759ea046b8e5cd3d4fa178 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 2651833f194b065326a5e063c17e2f0bc3ac2285..b21d072321464b72d3e20d4123f46b12235651d3 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 3d2d8361cb4f79ef0645058700dfff1d62aba354..cf71839f9f757d4ed0f2eadd950d5c3cef97f804 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 350faeabaa0d8565b6a5ef5d477716a09cb1e2c3..e4e60c6926c4afdab326a952d88facd2368f80be 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 a668a450d0b389652ce8ecb88a177ccce2d2014e..1271f4d212c29113f133f3e1a132cfc63c936b83 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 e19452a9ce134d3baedbfe0c8478726ee90da922..0000000000000000000000000000000000000000 --- 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 1e0bce3a26aeb5a027bf60c4c3af3256754402b8..0000000000000000000000000000000000000000 --- 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 40580191306895ae8d75f39ed5b261681fe354a4..79940a314c9b5c014e20b27d7b3967e6adc6de1f 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 ec3cb7ed7ff0dc198b3c295ee2bec54e282533b9..cef864152918f4c115dd13fb7aefb99d9c99c6cf 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<Boolean> resultHandler); - void changePassword(String password, String newPassword, + void changePassword(String oldPassword, String newPassword, ResultHandler<Boolean> 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 905797acc6eae94d09f1686da63db1dccacfc084..0ecf784756a1155c6620e870fdc855f2e73a504e 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<Boolean> 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<Boolean> 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 f91c57512f1c02e9a53d591d17f7dbdfbc68154e..e38cfeb0c9e2acda08155cc9be2be824f19dd768 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 3923cce4abf75284e3d0666059985122ce52f43b..02f40226e404806620b53c2df0323140cdccaa9c 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<Void> resultHandler = - new UiResultHandler<Void>(setupActivity) { + UiResultHandler<Boolean> resultHandler = + new UiResultHandler<Boolean>(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<Void> resultHandler) { + void createAccount(ResultHandler<Boolean> 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 cf1edaae668db3a49a938478a3e7633066e3e79e..0ed79eddfa93190a7264c23b55e88a55534cdab7 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 dd122e1498b958338de9931e462e13f8e90e1d48..9d64d15000e00ef02b7cbfb4e4b1d6f10200e723 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 d207508a40775081f98e990540caccf91d9e9906..e878d2376278c4ce83352f55bb9ef07301505d25 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 0e9173105fa19f99747e91c4c68d25fbb2bd2138..0000000000000000000000000000000000000000 --- 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 859792b3c9872de53ef0c8d03b430be30ef14f30..0000000000000000000000000000000000000000 --- 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 72b8d66448bfcff0d92accabdd47bf68ddc5f76f..7a04b4517bc2765b0e0d6d2f9e85e219d748088b 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 427dfa772d1cb52b75e2264344e8f4c2c8cf66d3..987155ffcb848003c9f52f018f2fbd3669ada52d 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 b0df4cde8b324b80c67a42f4656749b08828a5b8..2ee6d0594b28c2cfc24d17372a9e241acf1ef5d2 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 24c7334e72210cc96fa46ac4dfb4a379d46542ba..ced26a4a15d2ae19db7527d6bddbedfcee1137dd 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 76445fe03d18bc67993a40f2aa2a2a9d3ccf6225..685eedacba48c4a6286667c47f0f053ad521cc45 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 1007afe5e08866139119b5d3b3d129d5c1d8d434..c17f3a35d30aa1e5e61044c631dde3feb2b821e1 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 481a574869a56d0f455a7f6673b7bf1f56fb5f32..f10d9418304607ca097ca1dd142c298cb5c4b2c6 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<C extends BriarIntegrationTestCompone validationWaiter = new Waiter(); deliveryWaiter = new Waiter(); + createAndRegisterIdentities(); startLifecycles(); - getDefaultIdentities(); listenToEvents(); addDefaultContacts(); } @@ -193,9 +193,9 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone lifecycleManager0 = c0.getLifecycleManager(); lifecycleManager1 = c1.getLifecycleManager(); lifecycleManager2 = c2.getLifecycleManager(); - lifecycleManager0.startServices(AUTHOR0); - lifecycleManager1.startServices(AUTHOR1); - lifecycleManager2.startServices(AUTHOR2); + lifecycleManager0.startServices(getSecretKey()); + lifecycleManager1.startServices(getSecretKey()); + lifecycleManager2.startServices(getSecretKey()); lifecycleManager0.waitForStartup(); lifecycleManager1.waitForStartup(); lifecycleManager2.waitForStartup(); @@ -230,10 +230,13 @@ public abstract class BriarIntegrationTest<C extends BriarIntegrationTestCompone } } - private void getDefaultIdentities() throws DbException { - author0 = identityManager0.getLocalAuthor(); - author1 = identityManager1.getLocalAuthor(); - author2 = identityManager2.getLocalAuthor(); + private void createAndRegisterIdentities() { + author0 = identityManager0.createLocalAuthor(AUTHOR0); + identityManager0.registerLocalAuthor(author0); + author1 = identityManager1.createLocalAuthor(AUTHOR1); + identityManager1.registerLocalAuthor(author1); + author2 = identityManager2.createLocalAuthor(AUTHOR2); + identityManager2.registerLocalAuthor(author2); } protected void addDefaultContacts() throws Exception {