From 51cf49da19e89787744a18fbbce867f7eb97d5a0 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Wed, 11 Jul 2018 20:24:27 -0300 Subject: [PATCH] Create AccountManager to encapsulate authentication and account logic --- .../bramble/api/account/AccountManager.java | 39 ++ .../bramble/api/account/AccountState.java | 12 + .../bramble/api/db/DatabaseConfig.java | 5 - .../bramble/account/AccountManagerImpl.java | 127 ++++++ .../bramble/test/TestDatabaseConfig.java | 10 - briar-android/src/main/AndroidManifest.xml | 2 +- .../briar/android/AndroidComponent.java | 3 + .../briar/android/AndroidDatabaseConfig.java | 17 - .../briarproject/briar/android/AppModule.java | 9 + .../briar/android/BriarApplication.java | 3 + .../briar/android/BriarService.java | 12 +- .../AndroidAccountManagerImpl.java} | 55 ++- .../AuthorNameFragment.java | 2 +- .../{login => account}/DozeFragment.java | 4 +- .../android/{login => account}/DozeView.java | 2 +- .../{login => account}/HuaweiView.java | 2 +- .../{login => account}/PasswordFragment.java | 3 +- .../android/{login => account}/PowerView.java | 2 +- .../{login => account}/SetupActivity.java | 3 +- .../{login => account}/SetupController.java | 3 +- .../SetupControllerImpl.java | 18 +- .../{login => account}/SetupFragment.java | 4 +- .../android/activity/ActivityComponent.java | 8 +- .../android/activity/ActivityModule.java | 13 +- .../briar/android/activity/BriarActivity.java | 4 +- .../android/controller/BriarController.java | 2 +- .../controller/BriarControllerImpl.java | 18 +- .../android/controller/ConfigController.java | 23 -- .../briar/android/login/PasswordActivity.java | 5 +- .../android/login/PasswordController.java | 7 +- .../android/login/PasswordControllerImpl.java | 65 +--- .../android/login/SignInReminderReceiver.java | 7 +- .../android/panic/PanicResponderActivity.java | 6 +- .../android/splash/SplashScreenActivity.java | 14 +- .../AndroidAccountManagerImplTest.java | 368 ++++++++++++++++++ .../PasswordFragmentTest.java | 3 +- .../{login => account}/SetupActivityTest.java | 3 +- .../SetupControllerImplTest.java | 39 +- .../controller/ConfigControllerImplTest.java | 205 ---------- .../android/forum/TestForumActivity.java | 2 +- .../login/PasswordControllerImplTest.java | 92 +---- 41 files changed, 704 insertions(+), 517 deletions(-) create mode 100644 bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java create mode 100644 bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountState.java create mode 100644 bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java rename briar-android/src/main/java/org/briarproject/briar/android/{controller/ConfigControllerImpl.java => account/AndroidAccountManagerImpl.java} (76%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/AuthorNameFragment.java (98%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/DozeFragment.java (96%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/DozeView.java (96%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/HuaweiView.java (97%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/PasswordFragment.java (97%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/PowerView.java (98%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/SetupActivity.java (96%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/SetupController.java (85%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/SetupControllerImpl.java (84%) rename briar-android/src/main/java/org/briarproject/briar/android/{login => account}/SetupFragment.java (92%) delete mode 100644 briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java create mode 100644 briar-android/src/test/java/org/briarproject/briar/android/account/AndroidAccountManagerImplTest.java rename briar-android/src/test/java/org/briarproject/briar/android/{login => account}/PasswordFragmentTest.java (97%) rename briar-android/src/test/java/org/briarproject/briar/android/{login => account}/SetupActivityTest.java (93%) rename briar-android/src/test/java/org/briarproject/briar/android/{login => account}/SetupControllerImplTest.java (66%) delete mode 100644 briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java new file mode 100644 index 000000000..3c8519d89 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountManager.java @@ -0,0 +1,39 @@ +package org.briarproject.bramble.api.account; + +import org.briarproject.bramble.api.crypto.CryptoExecutor; +import org.briarproject.bramble.api.identity.IdentityManager; +import org.briarproject.bramble.api.identity.LocalAuthor; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import javax.annotation.Nullable; + +@NotNullByDefault +public interface AccountManager { + + @CryptoExecutor + void createAccount(String name, String password); + + AccountState getAccountState(); + + /** + * Returns the name of the {@link LocalAuthor} if it was just created and + * null otherwise. + * + * See {@link IdentityManager#getLocalAuthor()} for reliable retrieval. + */ + @Nullable + String getCreatedLocalAuthorName(); + + /** + * Validates the account password and returns true if it was valid. + */ + boolean validatePassword(String password); + + /** + * Changes the password and returns true if successful, false otherwise. + */ + @CryptoExecutor + boolean changePassword(String password, String newPassword); + + void deleteAccount(); +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountState.java b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountState.java new file mode 100644 index 000000000..2540c4d42 --- /dev/null +++ b/bramble-api/src/main/java/org/briarproject/bramble/api/account/AccountState.java @@ -0,0 +1,12 @@ +package org.briarproject.bramble.api.account; + +public enum AccountState { + + NO_ACCOUNT, + CREATING_ACCOUNT, + SIGNING_IN, + SIGNED_IN, + SIGNING_OUT, + SIGNED_OUT + +} diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java index 0be85eba9..e431c15f2 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 @@ -21,10 +21,5 @@ public interface DatabaseConfig { @Nullable SecretKey getEncryptionKey(); - void setLocalAuthorName(String nickname); - - @Nullable - String getLocalAuthorName(); - long getMaxSize(); } diff --git a/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java new file mode 100644 index 000000000..b77cfe71a --- /dev/null +++ b/bramble-core/src/main/java/org/briarproject/bramble/account/AccountManagerImpl.java @@ -0,0 +1,127 @@ +package org.briarproject.bramble.account; + + +import org.briarproject.bramble.api.account.AccountManager; +import org.briarproject.bramble.api.account.AccountState; +import org.briarproject.bramble.api.crypto.CryptoComponent; +import org.briarproject.bramble.api.crypto.CryptoExecutor; +import org.briarproject.bramble.api.crypto.SecretKey; +import org.briarproject.bramble.api.db.DatabaseConfig; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.util.StringUtils; + +import java.util.logging.Logger; + +import javax.annotation.Nullable; + +import static java.util.logging.Level.INFO; +import static org.briarproject.bramble.api.account.AccountState.CREATING_ACCOUNT; +import static org.briarproject.bramble.api.account.AccountState.NO_ACCOUNT; +import static org.briarproject.bramble.api.account.AccountState.SIGNED_IN; +import static org.briarproject.bramble.api.account.AccountState.SIGNED_OUT; +import static org.briarproject.bramble.util.LogUtils.logDuration; +import static org.briarproject.bramble.util.LogUtils.now; + +@NotNullByDefault +public abstract class AccountManagerImpl implements AccountManager { + + private final static Logger LOG = + Logger.getLogger(AccountManagerImpl.class.getSimpleName()); + + protected final DatabaseConfig databaseConfig; + private final CryptoComponent crypto; + @Nullable + private volatile String nickname = null; + + public AccountManagerImpl(CryptoComponent crypto, + DatabaseConfig databaseConfig) { + this.crypto = crypto; + this.databaseConfig = databaseConfig; + } + + protected abstract boolean storeEncryptedDatabaseKey(String hex); + + @Nullable + protected abstract String getEncryptedDatabaseKey(); + + private boolean hasEncryptedDatabaseKey() { + return getEncryptedDatabaseKey() != null; + } + + @Override + @CryptoExecutor + public void createAccount(String name, String password) { + LOG.info("Setting local author name"); + this.nickname = name; + SecretKey key = crypto.generateSecretKey(); + databaseConfig.setEncryptionKey(key); + String hex = encryptDatabaseKey(key, password); + storeEncryptedDatabaseKey(hex); + } + + @Override + public AccountState getAccountState() { + AccountState state; + if (!databaseConfig.databaseExists() && nickname != null && + hasEncryptedDatabaseKey()) { + state = CREATING_ACCOUNT; + } else if (!hasEncryptedDatabaseKey()) { + state = NO_ACCOUNT; + } else if (databaseConfig.getEncryptionKey() == null) { + state = SIGNED_OUT; + } else { + state = SIGNED_IN; + } + // TODO SIGNING_IN, SIGNING_OUT, DELETING_ACCOUNT + if (LOG.isLoggable(INFO)) LOG.info("Account State: " + state.name()); + return state; + } + + @Nullable + @Override + public String getCreatedLocalAuthorName() { + String nickname = this.nickname; + if (LOG.isLoggable(INFO)) + LOG.info("Local author name has been set: " + (nickname != null)); + return nickname; + } + + @CryptoExecutor + private 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); + } + + @Override + public boolean validatePassword(String password) { + byte[] encrypted = getEncryptedKeyAsBytes(); + byte[] key = crypto.decryptWithPassword(encrypted, password); + if (key == null) { + return false; + } + databaseConfig.setEncryptionKey(new SecretKey(key)); + return true; + } + + @Override + @CryptoExecutor + public boolean changePassword(String password, String newPassword) { + byte[] encrypted = getEncryptedKeyAsBytes(); + byte[] key = crypto.decryptWithPassword(encrypted, password); + if (key == null) { + return false; + } + String hex = encryptDatabaseKey(new SecretKey(key), newPassword); + return storeEncryptedDatabaseKey(hex); + } + + private byte[] getEncryptedKeyAsBytes() { + String hex = getEncryptedDatabaseKey(); + if (hex == null) + throw new IllegalStateException("Encrypted database key is null"); + return StringUtils.fromHexString(hex); + } + +} diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java index a830795f3..7e5ad34e7 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 @@ -46,16 +46,6 @@ public class TestDatabaseConfig implements DatabaseConfig { return key; } - @Override - public void setLocalAuthorName(String nickname) { - - } - - @Override - public String getLocalAuthorName() { - return null; - } - @Override public long getMaxSize() { return maxSize; diff --git a/briar-android/src/main/AndroidManifest.xml b/briar-android/src/main/AndroidManifest.xml index 9ae75930b..6e5257ab7 100644 --- a/briar-android/src/main/AndroidManifest.xml +++ b/briar-android/src/main/AndroidManifest.xml @@ -70,7 +70,7 @@ diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index 087aa5ea0..cd9057f7e 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 @@ -5,6 +5,7 @@ import android.content.SharedPreferences; import org.briarproject.bramble.BrambleAndroidModule; import org.briarproject.bramble.BrambleCoreEagerSingletons; import org.briarproject.bramble.BrambleCoreModule; +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; @@ -77,6 +78,8 @@ public interface AndroidComponent DatabaseConfig databaseConfig(); + AccountManager accountManager(); + @DatabaseExecutor Executor databaseExecutor(); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java index e92a356ee..1f21acdb0 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 @@ -21,8 +21,6 @@ class AndroidDatabaseConfig implements DatabaseConfig { @Nullable private volatile SecretKey key = null; - @Nullable - private volatile String nickname = null; AndroidDatabaseConfig(File dbDir, File keyDir) { this.dbDir = dbDir; @@ -70,21 +68,6 @@ class AndroidDatabaseConfig implements DatabaseConfig { 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() { 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 3f953fd2c..b98757867 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 @@ -5,6 +5,7 @@ import android.content.Context; import android.content.SharedPreferences; import android.os.StrictMode; +import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.db.DatabaseConfig; @@ -29,6 +30,7 @@ import org.briarproject.bramble.plugin.tor.CircumventionProvider; import org.briarproject.bramble.plugin.tor.TorPluginFactory; import org.briarproject.bramble.util.AndroidUtils; import org.briarproject.bramble.util.StringUtils; +import org.briarproject.briar.android.account.AndroidAccountManagerImpl; import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.DozeWatchdog; import org.briarproject.briar.api.android.ReferenceManager; @@ -94,6 +96,13 @@ public class AppModule { return databaseConfig; } + @Provides + @Singleton + AccountManager provideAccountManager( + AndroidAccountManagerImpl androidAccountManager) { + return androidAccountManager; + } + @Provides PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor, @Scheduler ScheduledExecutorService scheduler, diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java index 6ab1c9ee0..eb41caddb 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplication.java @@ -1,5 +1,6 @@ package org.briarproject.briar.android; +import android.content.Context; import android.content.SharedPreferences; import java.util.Collection; @@ -15,5 +16,7 @@ public interface BriarApplication { AndroidComponent getApplicationComponent(); + Context getApplicationContext(); + SharedPreferences getDefaultSharedPreferences(); } 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 523935278..9abb1fc12 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.account.AccountState; import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult; import org.briarproject.bramble.api.system.AndroidExecutor; @@ -48,6 +49,8 @@ import static android.support.v4.app.NotificationCompat.PRIORITY_MIN; import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.account.AccountState.CREATING_ACCOUNT; +import static org.briarproject.bramble.api.account.AccountState.SIGNED_IN; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.ALREADY_RUNNING; import static org.briarproject.bramble.api.lifecycle.LifecycleManager.StartResult.SUCCESS; import static org.briarproject.briar.api.android.AndroidNotificationManager.FAILURE_CHANNEL_ID; @@ -75,7 +78,7 @@ public class BriarService extends Service { private BroadcastReceiver receiver = null; @Inject - protected DatabaseConfig databaseConfig; + protected AccountManager accountManager; // Fields that are accessed from background threads must be volatile @Inject protected volatile LifecycleManager lifecycleManager; @@ -96,7 +99,8 @@ public class BriarService extends Service { stopSelf(); return; } - if (databaseConfig.getEncryptionKey() == null) { + AccountState accountState = accountManager.getAccountState(); + if (accountState != SIGNED_IN && accountState != CREATING_ACCOUNT) { LOG.info("No database key"); stopSelf(); return; @@ -141,7 +145,7 @@ public class BriarService extends Service { nm.cancel(REMINDER_NOTIFICATION_ID); // Start the services in a background thread new Thread(() -> { - String nickname = databaseConfig.getLocalAuthorName(); + String nickname = accountManager.getCreatedLocalAuthorName(); StartResult result = lifecycleManager.startServices(nickname); if (result == SUCCESS) { started = true; 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/account/AndroidAccountManagerImpl.java similarity index 76% rename from briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/AndroidAccountManagerImpl.java index 1e0bce3a2..f7ee1a5bc 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/AndroidAccountManagerImpl.java @@ -1,12 +1,13 @@ -package org.briarproject.briar.android.controller; +package org.briarproject.briar.android.account; -import android.content.Context; +import android.app.Application; import android.content.SharedPreferences; -import android.support.v7.preference.PreferenceManager; +import org.briarproject.bramble.account.AccountManagerImpl; +import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; -import org.briarproject.bramble.util.AndroidUtils; +import org.briarproject.briar.android.BriarApplication; import java.io.BufferedReader; import java.io.File; @@ -20,27 +21,30 @@ import javax.annotation.Nullable; import javax.inject.Inject; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.util.AndroidUtils.deleteAppData; import static org.briarproject.bramble.util.LogUtils.logException; @NotNullByDefault -public class ConfigControllerImpl implements ConfigController { +public class AndroidAccountManagerImpl extends AccountManagerImpl { - private static final Logger LOG = - Logger.getLogger(ConfigControllerImpl.class.getName()); + private final static Logger LOG = + Logger.getLogger(AndroidAccountManagerImpl.class.getSimpleName()); 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 BriarApplication app; + private final SharedPreferences dbPrefs; private final File dbKeyFile, dbKeyBackupFile; - protected final DatabaseConfig databaseConfig; @Inject - public ConfigControllerImpl(SharedPreferences briarPrefs, - DatabaseConfig databaseConfig) { - this.briarPrefs = briarPrefs; - this.databaseConfig = databaseConfig; + public AndroidAccountManagerImpl(CryptoComponent crypto, + DatabaseConfig databaseConfig, Application app, + SharedPreferences dbPrefs) { + super(crypto, databaseConfig); + this.app = (BriarApplication) app; + this.dbPrefs = dbPrefs; File keyDir = databaseConfig.getDatabaseKeyDirectory(); dbKeyFile = new File(keyDir, DB_KEY_FILENAME); dbKeyBackupFile = new File(keyDir, DB_KEY_BACKUP_FILENAME); @@ -48,7 +52,7 @@ public class ConfigControllerImpl implements ConfigController { @Override @Nullable - public String getEncryptedDatabaseKey() { + protected String getEncryptedDatabaseKey() { String key = getDatabaseKeyFromPreferences(); if (key == null) key = getDatabaseKeyFromFile(); else migrateDatabaseKeyToFile(key); @@ -57,7 +61,7 @@ public class ConfigControllerImpl implements ConfigController { @Nullable private String getDatabaseKeyFromPreferences() { - String key = briarPrefs.getString(PREF_DB_KEY, null); + String key = dbPrefs.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; @@ -97,7 +101,7 @@ public class ConfigControllerImpl implements ConfigController { private void migrateDatabaseKeyToFile(String key) { if (storeEncryptedDatabaseKey(key)) { - if (briarPrefs.edit().remove(PREF_DB_KEY).commit()) + if (dbPrefs.edit().remove(PREF_DB_KEY).commit()) LOG.info("Database key migrated to file"); else LOG.warning("Database key not removed from preferences"); } else { @@ -106,7 +110,7 @@ public class ConfigControllerImpl implements ConfigController { } @Override - public boolean storeEncryptedDatabaseKey(String hex) { + protected boolean storeEncryptedDatabaseKey(String hex) { LOG.info("Storing database key in file"); // Create the directory if necessary if (databaseConfig.getDatabaseKeyDirectory().mkdirs()) @@ -151,21 +155,10 @@ public class ConfigControllerImpl implements ConfigController { } @Override - public void deleteAccount(Context ctx) { + public void deleteAccount() { LOG.info("Deleting account"); - SharedPreferences defaultPrefs = - PreferenceManager.getDefaultSharedPreferences(ctx); - AndroidUtils.deleteAppData(ctx, briarPrefs, defaultPrefs); + SharedPreferences defaultPrefs = app.getDefaultSharedPreferences(); + deleteAppData(app.getApplicationContext(), dbPrefs, 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/AuthorNameFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/account/AuthorNameFragment.java similarity index 98% rename from briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/AuthorNameFragment.java index 6e4c35d86..c303c9739 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/AuthorNameFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/AuthorNameFragment.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.os.Bundle; import android.support.design.widget.TextInputEditText; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/account/DozeFragment.java similarity index 96% rename from briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/DozeFragment.java index 624437c6d..90e820748 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/DozeFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/DozeFragment.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.annotation.SuppressLint; import android.content.Intent; @@ -13,8 +13,8 @@ import android.widget.ProgressBar; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; +import org.briarproject.briar.android.account.PowerView.OnCheckedChangedListener; import org.briarproject.briar.android.activity.ActivityComponent; -import org.briarproject.briar.android.login.PowerView.OnCheckedChangedListener; import org.briarproject.briar.android.util.UiUtils; import static android.view.View.INVISIBLE; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/DozeView.java b/briar-android/src/main/java/org/briarproject/briar/android/account/DozeView.java similarity index 96% rename from briar-android/src/main/java/org/briarproject/briar/android/login/DozeView.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/DozeView.java index a2b1c5186..e02f4f2e5 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/DozeView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/DozeView.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.content.Context; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/HuaweiView.java b/briar-android/src/main/java/org/briarproject/briar/android/account/HuaweiView.java similarity index 97% rename from briar-android/src/main/java/org/briarproject/briar/android/login/HuaweiView.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/HuaweiView.java index c566ce697..2396aab25 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/HuaweiView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/HuaweiView.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.content.Context; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/account/PasswordFragment.java similarity index 97% rename from briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/PasswordFragment.java index 49c90239c..400a56d6c 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/PasswordFragment.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.os.Bundle; import android.os.IBinder; @@ -15,6 +15,7 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; +import org.briarproject.briar.android.login.StrengthMeter; import org.briarproject.briar.android.util.UiUtils; import javax.annotation.Nullable; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PowerView.java b/briar-android/src/main/java/org/briarproject/briar/android/account/PowerView.java similarity index 98% rename from briar-android/src/main/java/org/briarproject/briar/android/login/PowerView.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/PowerView.java index ffcaf3409..032826ca5 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/PowerView.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/PowerView.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.content.Context; import android.os.Parcel; 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/account/SetupActivity.java similarity index 96% rename from briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/SetupActivity.java index f91c57512..e655b6faa 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupActivity.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.annotation.TargetApi; import android.content.Intent; @@ -10,6 +10,7 @@ import org.briarproject.briar.R; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; +import org.briarproject.briar.android.login.OpenDatabaseActivity; import javax.annotation.Nullable; import javax.inject.Inject; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupController.java b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupController.java similarity index 85% rename from briar-android/src/main/java/org/briarproject/briar/android/login/SetupController.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/SetupController.java index 392151897..6df870ab6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupController.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupController.java @@ -1,6 +1,7 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.briar.android.login.PasswordController; @NotNullByDefault public interface SetupController extends PasswordController { 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/account/SetupControllerImpl.java similarity index 84% rename from briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/SetupControllerImpl.java index 3923cce4a..cf86f0d88 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupControllerImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupControllerImpl.java @@ -1,16 +1,15 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; -import android.content.SharedPreferences; import android.support.annotation.Nullable; +import org.briarproject.bramble.api.account.AccountManager; 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.crypto.SecretKey; -import org.briarproject.bramble.api.db.DatabaseConfig; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.briar.android.controller.handler.ResultHandler; import org.briarproject.briar.android.controller.handler.UiResultHandler; +import org.briarproject.briar.android.login.PasswordControllerImpl; import java.util.concurrent.Executor; import java.util.logging.Logger; @@ -28,11 +27,10 @@ public class SetupControllerImpl extends PasswordControllerImpl private volatile SetupActivity setupActivity; @Inject - SetupControllerImpl(SharedPreferences briarPrefs, - DatabaseConfig databaseConfig, + SetupControllerImpl(AccountManager accountManager, @CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto, PasswordStrengthEstimator strengthEstimator) { - super(briarPrefs, databaseConfig, cryptoExecutor, crypto, + super(accountManager, cryptoExecutor, crypto, strengthEstimator); } @@ -102,11 +100,7 @@ public class SetupControllerImpl extends PasswordControllerImpl if (password == null) throw new IllegalStateException(); cryptoExecutor.execute(() -> { LOG.info("Creating account"); - databaseConfig.setLocalAuthorName(authorName); - SecretKey key = crypto.generateSecretKey(); - databaseConfig.setEncryptionKey(key); - String hex = encryptDatabaseKey(key, password); - storeEncryptedDatabaseKey(hex); + accountManager.createAccount(authorName, password); resultHandler.onResult(null); }); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupFragment.java similarity index 92% rename from briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java rename to briar-android/src/main/java/org/briarproject/briar/android/account/SetupFragment.java index 5d3fce362..f12682f39 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/login/SetupFragment.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/account/SetupFragment.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.text.Editable; import android.text.TextWatcher; @@ -17,7 +17,7 @@ import javax.inject.Inject; import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog; -abstract class SetupFragment extends BaseFragment implements TextWatcher, +public abstract class SetupFragment extends BaseFragment implements TextWatcher, OnEditorActionListener, OnClickListener { @Inject diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java index 1d92a31f3..3abe3c7ef 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityComponent.java @@ -30,13 +30,13 @@ import org.briarproject.briar.android.keyagreement.ContactExchangeActivity; import org.briarproject.briar.android.keyagreement.IntroFragment; import org.briarproject.briar.android.keyagreement.KeyAgreementActivity; import org.briarproject.briar.android.keyagreement.KeyAgreementFragment; -import org.briarproject.briar.android.login.AuthorNameFragment; +import org.briarproject.briar.android.account.AuthorNameFragment; import org.briarproject.briar.android.login.ChangePasswordActivity; -import org.briarproject.briar.android.login.DozeFragment; +import org.briarproject.briar.android.account.DozeFragment; import org.briarproject.briar.android.login.OpenDatabaseActivity; import org.briarproject.briar.android.login.PasswordActivity; -import org.briarproject.briar.android.login.PasswordFragment; -import org.briarproject.briar.android.login.SetupActivity; +import org.briarproject.briar.android.account.PasswordFragment; +import org.briarproject.briar.android.account.SetupActivity; import org.briarproject.briar.android.navdrawer.NavDrawerActivity; import org.briarproject.briar.android.panic.PanicPreferencesActivity; import org.briarproject.briar.android.panic.PanicResponderActivity; diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/ActivityModule.java index 2651833f1..970e210e6 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,14 +4,12 @@ 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; import org.briarproject.briar.android.login.PasswordControllerImpl; -import org.briarproject.briar.android.login.SetupController; -import org.briarproject.briar.android.login.SetupControllerImpl; +import org.briarproject.briar.android.account.SetupController; +import org.briarproject.briar.android.account.SetupControllerImpl; import org.briarproject.briar.android.navdrawer.NavDrawerController; import org.briarproject.briar.android.navdrawer.NavDrawerControllerImpl; @@ -48,13 +46,6 @@ public class ActivityModule { return setupController; } - @ActivityScope - @Provides - ConfigController provideConfigController( - ConfigControllerImpl configController) { - return configController; - } - @ActivityScope @Provides PasswordController providePasswordController( diff --git a/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/activity/BriarActivity.java index 3d2d8361c..6d6469f5e 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.signedIn() && !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.signedIn()) { // Don't use UiResultHandler because we want the result even if // this activity has been destroyed briarController.signOut(result -> runOnUiThread( diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarController.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarController.java index 350faeaba..1a37a94f8 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 signedIn(); /** * Returns true via the handler when the app has dozed diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/BriarControllerImpl.java index a668a450d..291418cb5 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,8 @@ 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.account.AccountState; import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.settings.Settings; @@ -21,6 +22,8 @@ import java.util.logging.Logger; import javax.inject.Inject; import static java.util.logging.Level.WARNING; +import static org.briarproject.bramble.api.account.AccountState.CREATING_ACCOUNT; +import static org.briarproject.bramble.api.account.AccountState.SIGNED_IN; import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE; import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting; @@ -33,7 +36,7 @@ public class BriarControllerImpl implements BriarController { public static final String DOZE_ASK_AGAIN = "dozeAskAgain"; private final BriarServiceConnection serviceConnection; - private final DatabaseConfig databaseConfig; + private final AccountManager accountManager; @DatabaseExecutor private final Executor databaseExecutor; private final SettingsManager settingsManager; @@ -44,12 +47,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 +62,7 @@ public class BriarControllerImpl implements BriarController { @Override @CallSuper public void onActivityCreate(Activity activity) { - if (databaseConfig.getEncryptionKey() != null) startAndBindService(); + if (signedIn()) startAndBindService(); } @Override @@ -84,8 +87,9 @@ public class BriarControllerImpl implements BriarController { } @Override - public boolean hasEncryptionKey() { - return databaseConfig.getEncryptionKey() != null; + public boolean signedIn() { + AccountState state = accountManager.getAccountState(); + return state == CREATING_ACCOUNT || state == SIGNED_IN; } @Override diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java deleted file mode 100644 index e19452a9c..000000000 --- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java +++ /dev/null @@ -1,23 +0,0 @@ -package org.briarproject.briar.android.controller; - -import android.content.Context; - -import org.briarproject.bramble.api.nullsafety.NotNullByDefault; - -import javax.annotation.Nullable; - -@NotNullByDefault -public interface ConfigController { - - @Nullable - String getEncryptedDatabaseKey(); - - boolean storeEncryptedDatabaseKey(String hex); - - void deleteAccount(Context ctx); - - boolean accountExists(); - - boolean accountSignedIn(); - -} diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordActivity.java index f4368dd9a..5c8144c5a 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 @@ -14,6 +14,7 @@ import android.widget.ProgressBar; import org.briarproject.briar.R; import org.briarproject.briar.android.Localizer; +import org.briarproject.briar.android.account.SetupActivity; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BaseActivity; import org.briarproject.briar.android.controller.BriarController; @@ -85,7 +86,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.signedIn()) { setResult(RESULT_OK); finish(); } @@ -105,7 +106,7 @@ public class PasswordActivity extends BaseActivity { } private void deleteAccount() { - passwordController.deleteAccount(this); + passwordController.deleteAccount(); Localizer.reinitialize(); UiUtils.setTheme(this, getString(R.string.pref_theme_light_value)); setResult(RESULT_CANCELED); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordController.java index ec3cb7ed7..68f44ebeb 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,11 +1,12 @@ 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 { + + boolean accountExists(); float estimatePasswordStrength(String password); @@ -15,4 +16,6 @@ public interface PasswordController extends ConfigController { void changePassword(String password, String newPassword, ResultHandler resultHandler); + void deleteAccount(); + } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java index 905797acc..403f5b324 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,47 +1,41 @@ package org.briarproject.briar.android.login; -import android.content.SharedPreferences; - +import org.briarproject.bramble.api.account.AccountManager; 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.crypto.SecretKey; -import org.briarproject.bramble.api.db.DatabaseConfig; 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; +import static org.briarproject.bramble.api.account.AccountState.NO_ACCOUNT; @NotNullByDefault -public class PasswordControllerImpl extends ConfigControllerImpl - implements PasswordController { - - private static final Logger LOG = - Logger.getLogger(PasswordControllerImpl.class.getName()); +public class PasswordControllerImpl implements PasswordController { + protected final AccountManager accountManager; protected final Executor cryptoExecutor; protected final CryptoComponent crypto; private final PasswordStrengthEstimator strengthEstimator; @Inject - PasswordControllerImpl(SharedPreferences briarPrefs, - DatabaseConfig databaseConfig, + public PasswordControllerImpl(AccountManager accountManager, @CryptoExecutor Executor cryptoExecutor, CryptoComponent crypto, PasswordStrengthEstimator strengthEstimator) { - super(briarPrefs, databaseConfig); + this.accountManager = accountManager; this.cryptoExecutor = cryptoExecutor; this.crypto = crypto; this.strengthEstimator = strengthEstimator; } + @Override + public boolean accountExists() { + return accountManager.getAccountState() != NO_ACCOUNT; + } + @Override public float estimatePasswordStrength(String password) { return strengthEstimator.estimateStrength(password); @@ -50,46 +44,25 @@ public class PasswordControllerImpl extends ConfigControllerImpl @Override public void validatePassword(String password, ResultHandler resultHandler) { - byte[] encrypted = getEncryptedKey(); cryptoExecutor.execute(() -> { - byte[] key = crypto.decryptWithPassword(encrypted, password); - if (key == null) { - resultHandler.onResult(false); - } else { - databaseConfig.setEncryptionKey(new SecretKey(key)); - resultHandler.onResult(true); - } + boolean result = accountManager.validatePassword(password); + resultHandler.onResult(result); }); } @Override public void changePassword(String password, String newPassword, ResultHandler resultHandler) { - byte[] encrypted = getEncryptedKey(); cryptoExecutor.execute(() -> { - byte[] key = crypto.decryptWithPassword(encrypted, password); - if (key == null) { - resultHandler.onResult(false); - } else { - String hex = - encryptDatabaseKey(new SecretKey(key), newPassword); - resultHandler.onResult(storeEncryptedDatabaseKey(hex)); - } + boolean result = + accountManager.changePassword(password, newPassword); + resultHandler.onResult(result); }); } - private byte[] getEncryptedKey() { - String hex = getEncryptedDatabaseKey(); - if (hex == null) - throw new IllegalStateException("Encrypted database key is null"); - return StringUtils.fromHexString(hex); + @Override + public void deleteAccount() { + accountManager.deleteAccount(); } - @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/SignInReminderReceiver.java b/briar-android/src/main/java/org/briarproject/briar/android/login/SignInReminderReceiver.java index e361aeb6e..f6bec0821 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; @@ -28,6 +28,7 @@ import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK; import static android.os.Build.VERSION.SDK_INT; import static android.support.v4.app.NotificationCompat.PRIORITY_LOW; import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET; +import static org.briarproject.bramble.api.account.AccountState.NO_ACCOUNT; import static org.briarproject.briar.android.TestingConstants.FEATURE_FLAG_SIGN_IN_REMINDER; import static org.briarproject.briar.android.settings.SettingsActivity.NO_NOTIFY_SIGN_IN; import static org.briarproject.briar.android.settings.SettingsFragment.NOTIFY_SIGN_IN; @@ -37,7 +38,7 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.REMI public class SignInReminderReceiver extends BroadcastReceiver { @Inject - DatabaseConfig databaseConfig; + AccountManager accountManager; @Override public void onReceive(Context ctx, Intent intent) { @@ -51,7 +52,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.getAccountState() != NO_ACCOUNT) { SharedPreferences prefs = app.getDefaultSharedPreferences(); if (prefs.getBoolean(NOTIFY_SIGN_IN, true)) { showSignInNotification(ctx); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/panic/PanicResponderActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/panic/PanicResponderActivity.java index dd122e149..9d64d1500 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/panic/PanicResponderActivity.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/panic/PanicResponderActivity.java @@ -7,10 +7,10 @@ import android.os.Build; import android.os.Bundle; import android.support.v7.preference.PreferenceManager; +import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.system.AndroidExecutor; import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.BriarActivity; -import org.briarproject.briar.android.controller.ConfigController; import org.iilab.IilabEngineeringRSA2048Pin; import java.util.logging.Logger; @@ -33,7 +33,7 @@ public class PanicResponderActivity extends BriarActivity { Logger.getLogger(PanicResponderActivity.class.getName()); @Inject - protected ConfigController configController; + protected AccountManager accountManager; @Inject protected AndroidExecutor androidExecutor; @@ -94,7 +94,7 @@ public class PanicResponderActivity extends BriarActivity { private void deleteAllData() { androidExecutor.runOnBackgroundThread(() -> { - configController.deleteAccount(PanicResponderActivity.this); + accountManager.deleteAccount(); // TODO somehow delete/shred the database more thoroughly PanicResponder.deleteAllAppData(PanicResponderActivity.this); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java b/briar-android/src/main/java/org/briarproject/briar/android/splash/SplashScreenActivity.java index d207508a4..b2d61d882 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,18 +7,20 @@ 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.account.SetupActivity; 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; import java.util.logging.Logger; import javax.inject.Inject; +import static org.briarproject.bramble.api.account.AccountState.NO_ACCOUNT; +import static org.briarproject.bramble.api.account.AccountState.SIGNED_IN; import static org.briarproject.briar.android.TestingConstants.EXPIRY_DATE; public class SplashScreenActivity extends BaseActivity { @@ -27,7 +29,7 @@ public class SplashScreenActivity extends BaseActivity { Logger.getLogger(SplashScreenActivity.class.getName()); @Inject - protected ConfigController configController; + AccountManager accountManager; @Inject protected AndroidExecutor androidExecutor; @@ -43,7 +45,7 @@ public class SplashScreenActivity extends BaseActivity { setContentView(R.layout.splash); - if (configController.accountSignedIn()) { + if (accountManager.getAccountState() == SIGNED_IN) { startActivity(new Intent(this, OpenDatabaseActivity.class)); finish(); } else { @@ -64,12 +66,12 @@ public class SplashScreenActivity extends BaseActivity { LOG.info("Expired"); startActivity(new Intent(this, ExpiredActivity.class)); } else { - if (configController.accountExists()) { + if (accountManager.getAccountState() != NO_ACCOUNT) { 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/account/AndroidAccountManagerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/account/AndroidAccountManagerImplTest.java new file mode 100644 index 000000000..6d665085e --- /dev/null +++ b/briar-android/src/test/java/org/briarproject/briar/android/account/AndroidAccountManagerImplTest.java @@ -0,0 +1,368 @@ +package org.briarproject.briar.android.account; + +import android.app.Application; +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; + +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.test.BrambleMockTestCase; +import org.jmock.Expectations; +import org.junit.After; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +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.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; +import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey; +import static org.hamcrest.Matchers.samePropertyValuesAs; + +public class AndroidAccountManagerImplTest extends BrambleMockTestCase { + + private final CryptoComponent cryptoComponent = + context.mock(CryptoComponent.class); + 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 String authorName = getRandomString(MAX_AUTHOR_NAME_LENGTH); + private final String password = "some.strong.pass"; + private final String oldPassword = "some.old.pass"; + private final String newPassword = "some.new.pass"; + private final SecretKey key = getSecretKey(); + private final byte[] keyBytes = key.getBytes(); + private final byte[] encryptedKey = getRandomBytes(123); + private final String encryptedKeyHex = toHexString(encryptedKey); + private final String oldEncryptedKeyHex = toHexString(getRandomBytes(123)); + private final byte[] oldEncryptedKey = getRandomBytes(123); + private final byte[] newEncryptedKey = 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 createAccount() throws IOException { + context.checking(new Expectations() {{ + // Generate a database key + oneOf(cryptoComponent).generateSecretKey(); + will(returnValue(key)); + // Attach the author name and database key to the database config + oneOf(databaseConfig).setEncryptionKey(key); + // Encrypt the key with the password + oneOf(cryptoComponent) + .encryptWithPassword(key.getBytes(), password); + will(returnValue(encryptedKey)); + // Store the encrypted key + allowing(databaseConfig).getDatabaseKeyDirectory(); + will(returnValue(keyDir)); + }}); + getAndroidAccountManagerImpl().createAccount(authorName, password); + + assertTrue(keyFile.exists()); + assertTrue(keyBackupFile.exists()); + assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyFile)); + assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyBackupFile)); + } + + @Test + public void testChangePasswordReturnsTrue() throws Exception { + context.checking(new Expectations() {{ + // Look up the encrypted DB key + oneOf(prefs).getString("key", null); + will(returnValue(null)); + allowing(databaseConfig).getDatabaseKeyDirectory(); + will(returnValue(keyDir)); + // Decrypt and re-encrypt the key + oneOf(cryptoComponent) + .decryptWithPassword(oldEncryptedKey, oldPassword); + will(returnValue(key.getBytes())); + oneOf(cryptoComponent) + .encryptWithPassword(key.getBytes(), newPassword); + will(returnValue(newEncryptedKey)); + }}); + + assertFalse(keyFile.exists()); + assertFalse(keyBackupFile.exists()); + + storeDatabaseKey(keyFile, toHexString(oldEncryptedKey)); + storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey)); + + AndroidAccountManagerImpl accountManager = + getAndroidAccountManagerImpl(); + assertTrue(accountManager.changePassword(oldPassword, newPassword)); + + assertTrue(keyFile.exists()); + assertTrue(keyBackupFile.exists()); + assertEquals(toHexString(newEncryptedKey), loadDatabaseKey(keyFile)); + assertEquals(toHexString(newEncryptedKey), + loadDatabaseKey(keyBackupFile)); + } + + @Test + public void testChangePasswordReturnsFalseIfOldPasswordIsWrong() + throws Exception { + context.checking(new Expectations() {{ + // Look up the encrypted DB key + oneOf(prefs).getString("key", null); + will(returnValue(null)); + allowing(databaseConfig).getDatabaseKeyDirectory(); + will(returnValue(keyDir)); + // Try to decrypt the key - the password is wrong + oneOf(cryptoComponent) + .decryptWithPassword(oldEncryptedKey, oldPassword); + will(returnValue(null)); + }}); + + assertFalse(keyFile.exists()); + assertFalse(keyBackupFile.exists()); + + storeDatabaseKey(keyFile, toHexString(oldEncryptedKey)); + storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey)); + + AndroidAccountManagerImpl accountManager = + getAndroidAccountManagerImpl(); + assertFalse(accountManager.changePassword(oldPassword, newPassword)); + + assertTrue(keyFile.exists()); + assertTrue(keyBackupFile.exists()); + assertEquals(toHexString(oldEncryptedKey), loadDatabaseKey(keyFile)); + assertEquals(toHexString(oldEncryptedKey), + loadDatabaseKey(keyBackupFile)); + } + + @Test + public void testValidatePasswordReturnsTrue() throws Exception { + context.checking(new Expectations() {{ + // Look up the encrypted DB key + oneOf(prefs).getString("key", null); + will(returnValue(null)); + allowing(databaseConfig).getDatabaseKeyDirectory(); + will(returnValue(keyDir)); + // Decrypt the key + oneOf(cryptoComponent) + .decryptWithPassword(encryptedKey, password); + will(returnValue(keyBytes)); + oneOf(databaseConfig) + .setEncryptionKey(with(samePropertyValuesAs(key))); + }}); + + assertFalse(keyFile.exists()); + assertFalse(keyBackupFile.exists()); + + storeDatabaseKey(keyFile, toHexString(encryptedKey)); + storeDatabaseKey(keyBackupFile, toHexString(encryptedKey)); + + AndroidAccountManagerImpl accountManager = + getAndroidAccountManagerImpl(); + assertTrue(accountManager.validatePassword(password)); + } + + @Test + public void testValidatePasswordReturnsFalseIfPasswordIsWrong() + throws Exception { + context.checking(new Expectations() {{ + // Look up the encrypted DB key + oneOf(prefs).getString("key", null); + will(returnValue(null)); + allowing(databaseConfig).getDatabaseKeyDirectory(); + will(returnValue(keyDir)); + // Decrypt the key + oneOf(cryptoComponent) + .decryptWithPassword(encryptedKey, password); + will(returnValue(null)); + }}); + + assertFalse(keyFile.exists()); + assertFalse(keyBackupFile.exists()); + + storeDatabaseKey(keyFile, toHexString(encryptedKey)); + storeDatabaseKey(keyBackupFile, toHexString(encryptedKey)); + + AndroidAccountManagerImpl accountManager = + getAndroidAccountManagerImpl(); + assertFalse(accountManager.validatePassword(password)); + } + + @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()); + + AndroidAccountManagerImpl c = getAndroidAccountManagerImpl(); + + 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)); + + AndroidAccountManagerImpl c = getAndroidAccountManagerImpl(); + + 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)); + + AndroidAccountManagerImpl c = getAndroidAccountManagerImpl(); + + 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()); + + AndroidAccountManagerImpl c = getAndroidAccountManagerImpl(); + + 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)); + + AndroidAccountManagerImpl c = getAndroidAccountManagerImpl(); + + 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)); + + AndroidAccountManagerImpl c = getAndroidAccountManagerImpl(); + + assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex)); + + assertTrue(keyFile.exists()); + assertTrue(keyBackupFile.exists()); + assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); + assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); + } + + @After + public void tearDown() { + deleteTestDirectory(testDir); + } + + private AndroidAccountManagerImpl getAndroidAccountManagerImpl() { + // app is only needed for deleting account + Application app = null; + //noinspection ConstantConditions + return new AndroidAccountManagerImpl(cryptoComponent, databaseConfig, + app, prefs); + } +} diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java b/briar-android/src/test/java/org/briarproject/briar/android/account/PasswordFragmentTest.java similarity index 97% rename from briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java rename to briar-android/src/test/java/org/briarproject/briar/android/account/PasswordFragmentTest.java index 4ddc21cc9..9985a93c4 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordFragmentTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/account/PasswordFragmentTest.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.support.design.widget.TextInputLayout; import android.view.View; @@ -7,6 +7,7 @@ import android.widget.EditText; import org.briarproject.briar.R; import org.briarproject.briar.android.TestBriarApplication; +import org.briarproject.briar.android.login.StrengthMeter; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java b/briar-android/src/test/java/org/briarproject/briar/android/account/SetupActivityTest.java similarity index 93% rename from briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java rename to briar-android/src/test/java/org/briarproject/briar/android/account/SetupActivityTest.java index e01220686..8723dfc22 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupActivityTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/account/SetupActivityTest.java @@ -1,10 +1,11 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; import android.support.design.widget.TextInputLayout; import android.widget.EditText; import org.briarproject.briar.R; import org.briarproject.briar.android.TestBriarApplication; +import org.briarproject.briar.android.account.SetupActivity; import org.junit.Assert; import org.junit.Before; import org.junit.Test; 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/account/SetupControllerImplTest.java similarity index 66% rename from briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java rename to briar-android/src/test/java/org/briarproject/briar/android/account/SetupControllerImplTest.java index b0df4cde8..bd0ed5678 100644 --- a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java +++ b/briar-android/src/test/java/org/briarproject/briar/android/account/SetupControllerImplTest.java @@ -1,11 +1,10 @@ -package org.briarproject.briar.android.login; +package org.briarproject.briar.android.account; -import android.content.SharedPreferences; import android.content.pm.ApplicationInfo; +import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.crypto.CryptoComponent; 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; @@ -18,22 +17,17 @@ 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 AccountManager accountManager = + context.mock(AccountManager.class); private final DatabaseConfig databaseConfig = context.mock(DatabaseConfig.class); private final CryptoComponent crypto = context.mock(CryptoComponent.class); @@ -45,8 +39,6 @@ public class SetupControllerImplTest extends BrambleMockTestCase { 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"); @@ -74,25 +66,15 @@ 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)); + oneOf(accountManager).createAccount(authorName, password); }}); assertFalse(keyFile.exists()); assertFalse(keyBackupFile.exists()); - SetupControllerImpl s = new SetupControllerImpl(briarPrefs, - databaseConfig, cryptoExecutor, crypto, estimator); + SetupControllerImpl s = + new SetupControllerImpl(accountManager, cryptoExecutor, crypto, + estimator); s.setSetupActivity(setupActivity); AtomicBoolean called = new AtomicBoolean(false); @@ -100,11 +82,6 @@ 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 diff --git a/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java deleted file mode 100644 index 859792b3c..000000000 --- a/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java +++ /dev/null @@ -1,205 +0,0 @@ -package org.briarproject.briar.android.controller; - -import android.content.SharedPreferences; -import android.content.SharedPreferences.Editor; - -import org.briarproject.bramble.api.db.DatabaseConfig; -import org.briarproject.bramble.test.BrambleMockTestCase; -import org.jmock.Expectations; -import org.junit.After; -import org.junit.Test; - -import java.io.File; - -import static junit.framework.Assert.assertEquals; -import static junit.framework.Assert.assertFalse; -import static junit.framework.Assert.assertNull; -import static junit.framework.Assert.assertTrue; -import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory; -import static org.briarproject.bramble.test.TestUtils.getRandomBytes; -import static org.briarproject.bramble.test.TestUtils.getTestDirectory; -import static org.briarproject.bramble.util.StringUtils.toHexString; -import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey; -import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey; - -public class ConfigControllerImplTest extends BrambleMockTestCase { - - private final SharedPreferences prefs = - context.mock(SharedPreferences.class); - private final DatabaseConfig databaseConfig = - context.mock(DatabaseConfig.class); - private final Editor editor = context.mock(Editor.class); - - private final byte[] encryptedKey = getRandomBytes(123); - private final String encryptedKeyHex = toHexString(encryptedKey); - private final String oldEncryptedKeyHex = toHexString(getRandomBytes(123)); - private final File testDir = getTestDirectory(); - private final File keyDir = new File(testDir, "key"); - private final File keyFile = new File(keyDir, "db.key"); - private final File keyBackupFile = new File(keyDir, "db.key.bak"); - - @Test - public void testDbKeyIsMigratedFromPreferencesToFile() throws Exception { - context.checking(new Expectations() {{ - oneOf(prefs).getString("key", null); - will(returnValue(encryptedKeyHex)); - allowing(databaseConfig).getDatabaseKeyDirectory(); - will(returnValue(keyDir)); - oneOf(prefs).edit(); - will(returnValue(editor)); - oneOf(editor).remove("key"); - will(returnValue(editor)); - oneOf(editor).commit(); - will(returnValue(true)); - }}); - - assertFalse(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - - ConfigControllerImpl c = new ConfigControllerImpl(prefs, - databaseConfig); - - assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey()); - - assertTrue(keyFile.exists()); - assertTrue(keyBackupFile.exists()); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); - } - - @Test - public void testDbKeyIsLoadedFromPrimaryFile() throws Exception { - context.checking(new Expectations() {{ - oneOf(prefs).getString("key", null); - will(returnValue(null)); - allowing(databaseConfig).getDatabaseKeyDirectory(); - will(returnValue(keyDir)); - }}); - - assertFalse(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - - storeDatabaseKey(keyFile, encryptedKeyHex); - - assertTrue(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); - - ConfigControllerImpl c = new ConfigControllerImpl(prefs, - databaseConfig); - - assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey()); - - assertTrue(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); - } - - @Test - public void testDbKeyIsLoadedFromBackupFile() throws Exception { - context.checking(new Expectations() {{ - oneOf(prefs).getString("key", null); - will(returnValue(null)); - allowing(databaseConfig).getDatabaseKeyDirectory(); - will(returnValue(keyDir)); - }}); - - assertFalse(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - - storeDatabaseKey(keyBackupFile, encryptedKeyHex); - - assertFalse(keyFile.exists()); - assertTrue(keyBackupFile.exists()); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); - - ConfigControllerImpl c = new ConfigControllerImpl(prefs, - databaseConfig); - - assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey()); - - assertFalse(keyFile.exists()); - assertTrue(keyBackupFile.exists()); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); - } - - @Test - public void testDbKeyIsNullIfNotFound() { - context.checking(new Expectations() {{ - oneOf(prefs).getString("key", null); - will(returnValue(null)); - allowing(databaseConfig).getDatabaseKeyDirectory(); - will(returnValue(keyDir)); - }}); - - assertFalse(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - - ConfigControllerImpl c = new ConfigControllerImpl(prefs, - databaseConfig); - - assertNull(c.getEncryptedDatabaseKey()); - - assertFalse(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - } - - @Test - public void testStoringDbKeyOverwritesPrimary() throws Exception { - context.checking(new Expectations() {{ - allowing(databaseConfig).getDatabaseKeyDirectory(); - will(returnValue(keyDir)); - }}); - - assertFalse(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - - storeDatabaseKey(keyFile, oldEncryptedKeyHex); - - assertTrue(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyFile)); - - ConfigController c = new ConfigControllerImpl(prefs, - databaseConfig); - - assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex)); - - assertTrue(keyFile.exists()); - assertTrue(keyBackupFile.exists()); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); - } - - @Test - public void testStoringDbKeyOverwritesBackup() throws Exception { - context.checking(new Expectations() {{ - allowing(databaseConfig).getDatabaseKeyDirectory(); - will(returnValue(keyDir)); - }}); - - assertFalse(keyFile.exists()); - assertFalse(keyBackupFile.exists()); - - storeDatabaseKey(keyBackupFile, oldEncryptedKeyHex); - - assertFalse(keyFile.exists()); - assertTrue(keyBackupFile.exists()); - assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyBackupFile)); - - ConfigController c = new ConfigControllerImpl(prefs, - databaseConfig); - - assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex)); - - assertTrue(keyFile.exists()); - assertTrue(keyBackupFile.exists()); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile)); - assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile)); - } - - @After - public void tearDown() { - deleteTestDirectory(testDir); - } -} diff --git a/briar-android/src/test/java/org/briarproject/briar/android/forum/TestForumActivity.java b/briar-android/src/test/java/org/briarproject/briar/android/forum/TestForumActivity.java index 72b8d6644..29397a0de 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.signedIn()).thenReturn(true); return c; } diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java index 427dfa772..639a59ae2 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,37 +1,23 @@ package org.briarproject.briar.android.login; -import android.content.SharedPreferences; - +import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.crypto.CryptoComponent; 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; public class PasswordControllerImplTest extends BrambleMockTestCase { - private final SharedPreferences briarPrefs = - context.mock(SharedPreferences.class); - private final DatabaseConfig databaseConfig = - context.mock(DatabaseConfig.class); + private final AccountManager accountManager = + context.mock(AccountManager.class); private final CryptoComponent crypto = context.mock(CryptoComponent.class); private final PasswordStrengthEstimator estimator = context.mock(PasswordStrengthEstimator.class); @@ -40,85 +26,37 @@ public class PasswordControllerImplTest extends BrambleMockTestCase { 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"); @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, cryptoExecutor, + crypto, 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, cryptoExecutor, + crypto, 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); - } }