Skip to content
Snippets Groups Projects
Verified Commit abaefacb authored by akwizgran's avatar akwizgran
Browse files

Add javadocs.

parent 6ca0339d
No related branches found
No related tags found
No related merge requests found
......@@ -38,12 +38,15 @@ class AndroidAccountManager extends AccountManagerImpl
@Override
@Nullable
protected String loadEncryptedDatabaseKey() {
String key = getDatabaseKeyFromPreferences();
if (key == null) key = super.loadEncryptedDatabaseKey();
else migrateDatabaseKeyToFile(key);
return key;
synchronized (stateChangeLock) {
String key = getDatabaseKeyFromPreferences();
if (key == null) key = super.loadEncryptedDatabaseKey();
else migrateDatabaseKeyToFile(key);
return key;
}
}
// Locking: stateChangeLock
@Nullable
private String getDatabaseKeyFromPreferences() {
String key = prefs.getString(PREF_DB_KEY, null);
......@@ -52,6 +55,7 @@ class AndroidAccountManager extends AccountManagerImpl
return key;
}
// Locking: stateChangeLock
private void migrateDatabaseKeyToFile(String key) {
if (storeEncryptedDatabaseKey(key)) {
if (prefs.edit().remove(PREF_DB_KEY).commit())
......@@ -64,12 +68,15 @@ class AndroidAccountManager extends AccountManagerImpl
@Override
public void deleteAccount() {
super.deleteAccount();
SharedPreferences defaultPrefs =
PreferenceManager.getDefaultSharedPreferences(appContext);
deleteAppData(prefs, defaultPrefs);
synchronized (stateChangeLock) {
super.deleteAccount();
SharedPreferences defaultPrefs =
PreferenceManager.getDefaultSharedPreferences(appContext);
deleteAppData(prefs, defaultPrefs);
}
}
// Locking: stateChangeLock
private void deleteAppData(SharedPreferences... clear) {
// Clear and commit shared preferences
for (SharedPreferences prefs : clear) {
......
......@@ -8,18 +8,58 @@ import javax.annotation.Nullable;
@NotNullByDefault
public interface AccountManager {
/**
* Returns true if the manager has the database key. This will be false
* before {@link #createAccount(String)} 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();
/**
* 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
SecretKey getDatabaseKey();
/**
* Returns true if the encrypted database key can be loaded from disk and
* the database directory exists.
*/
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);
/**
* Deletes all account state from disk. {@link #accountExists()} will
* return false after this method returns.
*/
void deleteAccount();
/**
* Loads the encrypted database key from disk and decrypts it with the
* given password.
*
* @return true if the database key was successfully loaded and decrypted.
*/
boolean signIn(String password);
/**
* Loads the encrypted database key from disk, decrypts it with the old
* password, encrypts it with the new password, and stores it on disk,
* replacing the old key.
*
* @return true if the database key was successfully loaded, re-encrypted
* and stored.
*/
boolean changePassword(String oldPassword, String newPassword);
}
......@@ -38,6 +38,8 @@ class AccountManagerImpl implements AccountManager {
private final CryptoComponent crypto;
private final File dbKeyFile, dbKeyBackupFile;
protected final Object stateChangeLock = new Object();
@Nullable
private volatile SecretKey databaseKey = null;
......@@ -63,18 +65,21 @@ class AccountManagerImpl implements AccountManager {
@Nullable
protected String loadEncryptedDatabaseKey() {
String key = readDbKeyFromFile(dbKeyFile);
if (key == null) {
LOG.info("No database key in primary file");
key = readDbKeyFromFile(dbKeyBackupFile);
if (key == null) LOG.info("No database key in backup file");
else LOG.warning("Found database key in backup file");
} else {
LOG.info("Found database key in primary file");
synchronized (stateChangeLock) {
String key = readDbKeyFromFile(dbKeyFile);
if (key == null) {
LOG.info("No database key in primary file");
key = readDbKeyFromFile(dbKeyBackupFile);
if (key == null) LOG.info("No database key in backup file");
else LOG.warning("Found database key in backup file");
} else {
LOG.info("Found database key in primary file");
}
return key;
}
return key;
}
// Locking: stateChangeLock
@Nullable
private String readDbKeyFromFile(File f) {
if (!f.exists()) {
......@@ -93,6 +98,7 @@ class AccountManagerImpl implements AccountManager {
}
}
// Locking: stateChangeLock
protected boolean storeEncryptedDatabaseKey(String hex) {
LOG.info("Storing database key in file");
// Create the directory if necessary
......@@ -130,6 +136,7 @@ class AccountManagerImpl implements AccountManager {
}
}
// Locking: stateChangeLock
private void writeDbKeyToFile(String key, File f) throws IOException {
FileOutputStream out = new FileOutputStream(f);
out.write(key.getBytes("UTF-8"));
......@@ -139,18 +146,23 @@ class AccountManagerImpl implements AccountManager {
@Override
public boolean accountExists() {
return loadEncryptedDatabaseKey() != null
&& databaseConfig.getDatabaseDirectory().isDirectory();
synchronized (stateChangeLock) {
return loadEncryptedDatabaseKey() != null
&& databaseConfig.getDatabaseDirectory().isDirectory();
}
}
@Override
public boolean createAccount(String password) {
SecretKey key = crypto.generateSecretKey();
if (!encryptAndStoreDatabaseKey(key, password)) return false;
databaseKey = key;
return true;
synchronized (stateChangeLock) {
SecretKey key = crypto.generateSecretKey();
if (!encryptAndStoreDatabaseKey(key, password)) return false;
databaseKey = key;
return true;
}
}
// Locking: stateChangeLock
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
byte[] plaintext = key.getBytes();
byte[] ciphertext = crypto.encryptWithPassword(plaintext, password);
......@@ -159,19 +171,24 @@ class AccountManagerImpl implements AccountManager {
@Override
public void deleteAccount() {
LOG.info("Deleting account");
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory());
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory());
synchronized (stateChangeLock) {
LOG.info("Deleting account");
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseKeyDirectory());
IoUtils.deleteFileOrDir(databaseConfig.getDatabaseDirectory());
}
}
@Override
public boolean signIn(String password) {
SecretKey key = loadAndDecryptDatabaseKey(password);
if (key == null) return false;
databaseKey = key;
return true;
synchronized (stateChangeLock) {
SecretKey key = loadAndDecryptDatabaseKey(password);
if (key == null) return false;
databaseKey = key;
return true;
}
}
// Locking: stateChangeLock
@Nullable
private SecretKey loadAndDecryptDatabaseKey(String password) {
String hex = loadEncryptedDatabaseKey();
......@@ -190,7 +207,9 @@ class AccountManagerImpl implements AccountManager {
@Override
public boolean changePassword(String oldPassword, String newPassword) {
SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
return key != null && encryptAndStoreDatabaseKey(key, newPassword);
synchronized (stateChangeLock) {
SecretKey key = loadAndDecryptDatabaseKey(oldPassword);
return key != null && encryptAndStoreDatabaseKey(key, newPassword);
}
}
}
......@@ -53,6 +53,7 @@ public class PasswordActivity extends BaseActivity {
overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
if (!accountManager.accountExists()) {
// TODO: Finish instead of deleting account?
deleteAccount();
return;
}
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment