Add javadocs.

This commit is contained in:
akwizgran
2018-07-27 12:06:55 +01:00
parent 6ca0339da2
commit abaefacb69
4 changed files with 99 additions and 32 deletions

View File

@@ -38,12 +38,15 @@ class AndroidAccountManager extends AccountManagerImpl
@Override @Override
@Nullable @Nullable
protected String loadEncryptedDatabaseKey() { protected String loadEncryptedDatabaseKey() {
String key = getDatabaseKeyFromPreferences(); synchronized (stateChangeLock) {
if (key == null) key = super.loadEncryptedDatabaseKey(); String key = getDatabaseKeyFromPreferences();
else migrateDatabaseKeyToFile(key); if (key == null) key = super.loadEncryptedDatabaseKey();
return key; else migrateDatabaseKeyToFile(key);
return key;
}
} }
// Locking: stateChangeLock
@Nullable @Nullable
private String getDatabaseKeyFromPreferences() { private String getDatabaseKeyFromPreferences() {
String key = prefs.getString(PREF_DB_KEY, null); String key = prefs.getString(PREF_DB_KEY, null);
@@ -52,6 +55,7 @@ class AndroidAccountManager extends AccountManagerImpl
return key; return key;
} }
// Locking: stateChangeLock
private void migrateDatabaseKeyToFile(String key) { private void migrateDatabaseKeyToFile(String key) {
if (storeEncryptedDatabaseKey(key)) { if (storeEncryptedDatabaseKey(key)) {
if (prefs.edit().remove(PREF_DB_KEY).commit()) if (prefs.edit().remove(PREF_DB_KEY).commit())
@@ -64,12 +68,15 @@ class AndroidAccountManager extends AccountManagerImpl
@Override @Override
public void deleteAccount() { public void deleteAccount() {
super.deleteAccount(); synchronized (stateChangeLock) {
SharedPreferences defaultPrefs = super.deleteAccount();
PreferenceManager.getDefaultSharedPreferences(appContext); SharedPreferences defaultPrefs =
deleteAppData(prefs, defaultPrefs); PreferenceManager.getDefaultSharedPreferences(appContext);
deleteAppData(prefs, defaultPrefs);
}
} }
// Locking: stateChangeLock
private void deleteAppData(SharedPreferences... clear) { private void deleteAppData(SharedPreferences... clear) {
// Clear and commit shared preferences // Clear and commit shared preferences
for (SharedPreferences prefs : clear) { for (SharedPreferences prefs : clear) {

View File

@@ -8,18 +8,58 @@ import javax.annotation.Nullable;
@NotNullByDefault @NotNullByDefault
public interface AccountManager { public interface AccountManager {
/**
* Returns true if the manager has the database key. This will be false
* before {@link #createAccount(String)} or {@link #signIn(String)} has
* been called, and true after {@link #createAccount(String)} or
* {@link #signIn(String)} has returned, until the process exits.
*/
boolean hasDatabaseKey(); boolean hasDatabaseKey();
/**
* Returns the database key if the manager has it. This will be null
* before {@link #createAccount(String)} or {@link #signIn(String)} has
* been called, and non-null after {@link #createAccount(String)} or
* {@link #signIn(String)} has returned, until the process exits.
*/
@Nullable @Nullable
SecretKey getDatabaseKey(); SecretKey getDatabaseKey();
/**
* Returns true if the encrypted database key can be loaded from disk and
* the database directory exists.
*/
boolean accountExists(); boolean accountExists();
/**
* Creates a database key, encrypts it with the given password and stores
* it on disk. This method does not create the database directory, so
* {@link #accountExists()} will continue to return false until the
* database directory is created.
*/
boolean createAccount(String password); boolean createAccount(String password);
/**
* Deletes all account state from disk. {@link #accountExists()} will
* return false after this method returns.
*/
void deleteAccount(); 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); 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); boolean changePassword(String oldPassword, String newPassword);
} }

View File

@@ -38,6 +38,8 @@ class AccountManagerImpl implements AccountManager {
private final CryptoComponent crypto; private final CryptoComponent crypto;
private final File dbKeyFile, dbKeyBackupFile; private final File dbKeyFile, dbKeyBackupFile;
protected final Object stateChangeLock = new Object();
@Nullable @Nullable
private volatile SecretKey databaseKey = null; private volatile SecretKey databaseKey = null;
@@ -63,18 +65,21 @@ class AccountManagerImpl implements AccountManager {
@Nullable @Nullable
protected String loadEncryptedDatabaseKey() { protected String loadEncryptedDatabaseKey() {
String key = readDbKeyFromFile(dbKeyFile); synchronized (stateChangeLock) {
if (key == null) { String key = readDbKeyFromFile(dbKeyFile);
LOG.info("No database key in primary file"); if (key == null) {
key = readDbKeyFromFile(dbKeyBackupFile); LOG.info("No database key in primary file");
if (key == null) LOG.info("No database key in backup file"); key = readDbKeyFromFile(dbKeyBackupFile);
else LOG.warning("Found database key in backup file"); if (key == null) LOG.info("No database key in backup file");
} else { else LOG.warning("Found database key in backup file");
LOG.info("Found database key in primary file"); } else {
LOG.info("Found database key in primary file");
}
return key;
} }
return key;
} }
// Locking: stateChangeLock
@Nullable @Nullable
private String readDbKeyFromFile(File f) { private String readDbKeyFromFile(File f) {
if (!f.exists()) { if (!f.exists()) {
@@ -93,6 +98,7 @@ class AccountManagerImpl implements AccountManager {
} }
} }
// Locking: stateChangeLock
protected boolean storeEncryptedDatabaseKey(String hex) { protected boolean storeEncryptedDatabaseKey(String hex) {
LOG.info("Storing database key in file"); LOG.info("Storing database key in file");
// Create the directory if necessary // Create the directory if necessary
@@ -130,6 +136,7 @@ class AccountManagerImpl implements AccountManager {
} }
} }
// Locking: stateChangeLock
private void writeDbKeyToFile(String key, File f) throws IOException { private void writeDbKeyToFile(String key, File f) throws IOException {
FileOutputStream out = new FileOutputStream(f); FileOutputStream out = new FileOutputStream(f);
out.write(key.getBytes("UTF-8")); out.write(key.getBytes("UTF-8"));
@@ -139,18 +146,23 @@ class AccountManagerImpl implements AccountManager {
@Override @Override
public boolean accountExists() { public boolean accountExists() {
return loadEncryptedDatabaseKey() != null synchronized (stateChangeLock) {
&& databaseConfig.getDatabaseDirectory().isDirectory(); return loadEncryptedDatabaseKey() != null
&& databaseConfig.getDatabaseDirectory().isDirectory();
}
} }
@Override @Override
public boolean createAccount(String password) { public boolean createAccount(String password) {
SecretKey key = crypto.generateSecretKey(); synchronized (stateChangeLock) {
if (!encryptAndStoreDatabaseKey(key, password)) return false; SecretKey key = crypto.generateSecretKey();
databaseKey = key; if (!encryptAndStoreDatabaseKey(key, password)) return false;
return true; databaseKey = key;
return true;
}
} }
// Locking: stateChangeLock
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) { private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
byte[] plaintext = key.getBytes(); byte[] plaintext = key.getBytes();
byte[] ciphertext = crypto.encryptWithPassword(plaintext, password); byte[] ciphertext = crypto.encryptWithPassword(plaintext, password);
@@ -159,19 +171,24 @@ class AccountManagerImpl implements AccountManager {
@Override @Override
public void deleteAccount() { public void deleteAccount() {
LOG.info("Deleting account"); synchronized (stateChangeLock) {
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory()); LOG.info("Deleting account");
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory()); IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory());
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory());
}
} }
@Override @Override
public boolean signIn(String password) { public boolean signIn(String password) {
SecretKey key = loadAndDecryptDatabaseKey(password); synchronized (stateChangeLock) {
if (key == null) return false; SecretKey key = loadAndDecryptDatabaseKey(password);
databaseKey = key; if (key == null) return false;
return true; databaseKey = key;
return true;
}
} }
// Locking: stateChangeLock
@Nullable @Nullable
private SecretKey loadAndDecryptDatabaseKey(String password) { private SecretKey loadAndDecryptDatabaseKey(String password) {
String hex = loadEncryptedDatabaseKey(); String hex = loadEncryptedDatabaseKey();
@@ -190,7 +207,9 @@ class AccountManagerImpl implements AccountManager {
@Override @Override
public boolean changePassword(String oldPassword, String newPassword) { public boolean changePassword(String oldPassword, String newPassword) {
SecretKey key = loadAndDecryptDatabaseKey(oldPassword); synchronized (stateChangeLock) {
return key != null && encryptAndStoreDatabaseKey(key, newPassword); SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
return key != null && encryptAndStoreDatabaseKey(key, newPassword);
}
} }
} }

View File

@@ -53,6 +53,7 @@ public class PasswordActivity extends BaseActivity {
overridePendingTransition(R.anim.fade_in, R.anim.fade_out); overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
if (!accountManager.accountExists()) { if (!accountManager.accountExists()) {
// TODO: Finish instead of deleting account?
deleteAccount(); deleteAccount();
return; return;
} }