From a20dc326bc78fac75d599f0b755c23d4d9cbc775 Mon Sep 17 00:00:00 2001 From: Jakob Nixdorf Date: Wed, 3 Jan 2018 13:23:13 +0100 Subject: [PATCH] Try the encryption change in the SettingsActivity and restore backup if it fails --- .../Activities/AuthenticateActivity.java | 34 ++---- .../andotp/Activities/BackupActivity.java | 7 +- .../andotp/Activities/MainActivity.java | 36 ++---- .../andotp/Activities/SettingsActivity.java | 113 +++++++++++++++--- .../Preferences/CredentialsPreference.java | 27 ++--- .../andotp/Utilities/DatabaseHelper.java | 45 +++++++ app/src/main/res/values/strings_settings.xml | 11 ++ 7 files changed, 193 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/AuthenticateActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/AuthenticateActivity.java index 07bae0a5..16f1c295 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/AuthenticateActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/AuthenticateActivity.java @@ -53,16 +53,13 @@ import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod; public class AuthenticateActivity extends ThemedActivity implements EditText.OnEditorActionListener { public static final String AUTH_EXTRA_NAME_PASSWORD_KEY = "password_key"; - public static final String AUTH_EXTRA_NAME_FATAL = "fatal"; - public static final String AUTH_EXTRA_NAME_SAVE_DATABASE = "save_database"; + public static final String AUTH_EXTRA_NAME_NEW_ENCRYPTION = "new_encryption"; public static final String AUTH_EXTRA_NAME_MESSAGE = "message"; - boolean saveDatabase = false; - boolean fatal = true; - private String password; AuthMethod authMethod; + String newEncryption = ""; boolean oldPassword = false; TextInputEditText passwordInput; @@ -84,8 +81,7 @@ public class AuthenticateActivity extends ThemedActivity Intent callingIntent = getIntent(); int labelMsg = callingIntent.getIntExtra(AUTH_EXTRA_NAME_MESSAGE, R.string.auth_msg_authenticate); - saveDatabase = callingIntent.getBooleanExtra(AUTH_EXTRA_NAME_SAVE_DATABASE, false); - fatal = callingIntent.getBooleanExtra(AUTH_EXTRA_NAME_FATAL, true); + newEncryption = callingIntent.getStringExtra(AUTH_EXTRA_NAME_NEW_ENCRYPTION); TextView passwordLabel = v.findViewById(R.id.passwordLabel); TextInputLayout passwordLayout = v.findViewById(R.id.passwordLayout); @@ -173,26 +169,18 @@ public class AuthenticateActivity extends ThemedActivity // End with a result public void finishWithResult(boolean success, byte[] key) { - if (success || fatal) { - Intent data = new Intent(); + Intent data = new Intent(); - data.putExtra(AUTH_EXTRA_NAME_SAVE_DATABASE, saveDatabase); + if (newEncryption != null && ! newEncryption.isEmpty()) + data.putExtra(AUTH_EXTRA_NAME_NEW_ENCRYPTION, newEncryption); - if (key != null) - data.putExtra(AUTH_EXTRA_NAME_PASSWORD_KEY, key); + if (key != null) + data.putExtra(AUTH_EXTRA_NAME_PASSWORD_KEY, key); - if (success) - setResult(RESULT_OK, data); + if (success) + setResult(RESULT_OK, data); - finish(); - } else { - passwordInput.setText(""); - - if (authMethod == AuthMethod.PASSWORD) - Toast.makeText(this, R.string.auth_toast_password_again, Toast.LENGTH_LONG).show(); - else if (authMethod == AuthMethod.PIN) - Toast.makeText(this, R.string.auth_toast_pin_again, Toast.LENGTH_LONG).show(); - } + finish(); } // Go back to the main activity diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java index cf14b600..b4926acc 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/BackupActivity.java @@ -62,7 +62,6 @@ import java.nio.charset.StandardCharsets; import java.util.ArrayList; import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; public class BackupActivity extends BaseActivity { private final static int INTENT_OPEN_DOCUMENT_PLAIN = 200; @@ -88,7 +87,7 @@ public class BackupActivity extends BaseActivity { private static final String DEFAULT_BACKUP_MIMETYPE_CRYPT = "binary/aes"; private static final String DEFAULT_BACKUP_MIMETYPE_PGP = "application/pgp-encrypted"; - public static final String EXTRA_NAME_ENCRYPTION_KEY = "encryption_key"; + public static final String BACKUP_EXTRA_NAME_ENCRYPTION_KEY = "encryption_key"; private SecretKey encryptionKey = null; @@ -117,8 +116,8 @@ public class BackupActivity extends BaseActivity { View v = stub.inflate(); Intent callingIntent = getIntent(); - byte[] keyMaterial = callingIntent.getByteArrayExtra(EXTRA_NAME_ENCRYPTION_KEY); - encryptionKey = new SecretKeySpec(keyMaterial, 0, keyMaterial.length, "AES"); + byte[] keyMaterial = callingIntent.getByteArrayExtra(BACKUP_EXTRA_NAME_ENCRYPTION_KEY); + encryptionKey = EncryptionHelper.generateSymmetricKey(keyMaterial); // Plain-text diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/MainActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/MainActivity.java index be629436..00381cef 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/MainActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/MainActivity.java @@ -72,11 +72,9 @@ import java.util.HashMap; import javax.crypto.SecretKey; -import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_FATAL; import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_MESSAGE; import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_PASSWORD_KEY; -import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_SAVE_DATABASE; -import static org.shadowice.flocke.andotp.Activities.BackupActivity.EXTRA_NAME_ENCRYPTION_KEY; +import static org.shadowice.flocke.andotp.Activities.BackupActivity.BACKUP_EXTRA_NAME_ENCRYPTION_KEY; import static org.shadowice.flocke.andotp.Activities.SettingsActivity.SETTINGS_EXTRA_NAME_ENCRYPTION_CHANGED; import static org.shadowice.flocke.andotp.Activities.SettingsActivity.SETTINGS_EXTRA_NAME_ENCRYPTION_KEY; import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod; @@ -130,7 +128,7 @@ public class MainActivity extends BaseActivity .show(); } - public void authenticate(int messageId, boolean saveDatabase, boolean fatal) { + public void authenticate(int messageId) { AuthMethod authMethod = settings.getAuthMethod(); if (authMethod == AuthMethod.DEVICE) { @@ -141,8 +139,6 @@ public class MainActivity extends BaseActivity } } else if (authMethod == AuthMethod.PASSWORD || authMethod == AuthMethod.PIN) { Intent authIntent = new Intent(this, AuthenticateActivity.class); - authIntent.putExtra(AUTH_EXTRA_NAME_SAVE_DATABASE, saveDatabase); - authIntent.putExtra(AUTH_EXTRA_NAME_FATAL, fatal); authIntent.putExtra(AUTH_EXTRA_NAME_MESSAGE, messageId); startActivityForResult(authIntent, INTENT_INTERNAL_AUTHENTICATE); } @@ -324,7 +320,7 @@ public class MainActivity extends BaseActivity if (requireAuthentication) { if (settings.getAuthMethod() != AuthMethod.NONE) { requireAuthentication = false; - authenticate(R.string.auth_msg_authenticate,false, true); + authenticate(R.string.auth_msg_authenticate); } } else { if (encryptionType == EncryptionType.KEYSTORE) { @@ -335,7 +331,7 @@ public class MainActivity extends BaseActivity populateAdapter(); } else if (encryptionType == EncryptionType.PASSWORD) { if (adapter.getEncryptionKey() == null) { - authenticate(R.string.auth_msg_authenticate,false, true); + authenticate(R.string.auth_msg_authenticate); } else { populateAdapter(); } @@ -396,11 +392,10 @@ public class MainActivity extends BaseActivity } } else if (requestCode == INTENT_INTERNAL_SETTINGS && resultCode == RESULT_OK) { boolean encryptionChanged = intent.getBooleanExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_CHANGED, false); + byte[] newKey = intent.getByteArrayExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY); - if (encryptionChanged) { - byte[] newKey = intent.getByteArrayExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY); - updateEncryption(newKey, true); - } + if (encryptionChanged) + updateEncryption(newKey); } else if (requestCode == INTENT_INTERNAL_AUTHENTICATE) { if (resultCode != RESULT_OK) { Toast.makeText(getBaseContext(), R.string.toast_auth_failed_fatal, Toast.LENGTH_LONG).show(); @@ -413,15 +408,13 @@ public class MainActivity extends BaseActivity } else { requireAuthentication = false; - boolean saveDatabase = intent.getBooleanExtra(AUTH_EXTRA_NAME_SAVE_DATABASE, false); byte[] authKey = intent.getByteArrayExtra(AUTH_EXTRA_NAME_PASSWORD_KEY); - - updateEncryption(authKey, saveDatabase); + updateEncryption(authKey); } } } - private void updateEncryption(byte[] newKey, boolean saveDatabase) { + private void updateEncryption(byte[] newKey) { SecretKey encryptionKey = null; encryptionType = settings.getEncryption(); @@ -432,17 +425,13 @@ public class MainActivity extends BaseActivity if (newKey != null && newKey.length > 0) { encryptionKey = EncryptionHelper.generateSymmetricKey(newKey); } else { - authenticate(R.string.auth_msg_confirm_encryption, true, false); + authenticate(R.string.auth_msg_confirm_encryption); } } - if (encryptionKey != null) { + if (encryptionKey != null) adapter.setEncryptionKey(encryptionKey); - if (saveDatabase) - adapter.saveEntries(); - } - populateAdapter(); } @@ -520,10 +509,11 @@ public class MainActivity extends BaseActivity if (id == R.id.action_backup) { Intent backupIntent = new Intent(this, BackupActivity.class); - backupIntent.putExtra(EXTRA_NAME_ENCRYPTION_KEY, adapter.getEncryptionKey().getEncoded()); + backupIntent.putExtra(BACKUP_EXTRA_NAME_ENCRYPTION_KEY, adapter.getEncryptionKey().getEncoded()); startActivityForResult(backupIntent, INTENT_INTERNAL_BACKUP); } else if (id == R.id.action_settings) { Intent settingsIntent = new Intent(this, SettingsActivity.class); + settingsIntent.putExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY, adapter.getEncryptionKey().getEncoded()); startActivityForResult(settingsIntent, INTENT_INTERNAL_SETTINGS); } else if (id == R.id.action_about){ Intent aboutIntent = new Intent(this, AboutActivity.class); diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java b/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java index 09fb9439..3e14fd0c 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Activities/SettingsActivity.java @@ -36,21 +36,36 @@ import android.widget.Toast; import org.openintents.openpgp.util.OpenPgpAppPreference; import org.openintents.openpgp.util.OpenPgpKeyPreference; +import org.shadowice.flocke.andotp.Database.Entry; import org.shadowice.flocke.andotp.Preferences.CredentialsPreference; import org.shadowice.flocke.andotp.R; +import org.shadowice.flocke.andotp.Utilities.DatabaseHelper; +import org.shadowice.flocke.andotp.Utilities.EncryptionHelper; import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper; import org.shadowice.flocke.andotp.Utilities.Settings; +import java.util.ArrayList; + +import javax.crypto.SecretKey; + +import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_MESSAGE; +import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_NEW_ENCRYPTION; +import static org.shadowice.flocke.andotp.Activities.AuthenticateActivity.AUTH_EXTRA_NAME_PASSWORD_KEY; import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod; import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType; public class SettingsActivity extends BaseActivity implements SharedPreferences.OnSharedPreferenceChangeListener{ + private static final int INTENT_INTERNAL_AUTHENTICATE = 300; + public static final String SETTINGS_EXTRA_NAME_ENCRYPTION_CHANGED = "encryption_changed"; public static final String SETTINGS_EXTRA_NAME_ENCRYPTION_KEY = "encryption_key"; SettingsFragment fragment; + SecretKey encryptionKey = null; + boolean encryptionChanged = false; + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -64,6 +79,10 @@ public class SettingsActivity extends BaseActivity ViewStub stub = findViewById(R.id.container_stub); stub.inflate(); + Intent callingIntent = getIntent(); + byte[] keyMaterial = callingIntent.getByteArrayExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY); + encryptionKey = EncryptionHelper.generateSymmetricKey(keyMaterial); + fragment = new SettingsFragment(); getFragmentManager().beginTransaction() @@ -74,13 +93,11 @@ public class SettingsActivity extends BaseActivity sharedPref.registerOnSharedPreferenceChangeListener(this); } - public void finishWithResult(boolean encryptionChanged, byte[] newKey) { + public void finishWithResult() { Intent data = new Intent(); data.putExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_CHANGED, encryptionChanged); - - if (newKey != null) - data.putExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY, newKey); + data.putExtra(SETTINGS_EXTRA_NAME_ENCRYPTION_KEY, encryptionKey.getEncoded()); setResult(RESULT_OK, data); finish(); @@ -88,13 +105,13 @@ public class SettingsActivity extends BaseActivity @Override public boolean onSupportNavigateUp() { - finishWithResult(false,null); + finishWithResult(); return true; } @Override public void onBackPressed() { - finishWithResult(false, null); + finishWithResult(); super.onBackPressed(); } @@ -106,10 +123,75 @@ public class SettingsActivity extends BaseActivity } } + private void tryEncryptionChangeWithAuth(EncryptionType newEnc) { + Intent authIntent = new Intent(this, AuthenticateActivity.class); + authIntent.putExtra(AUTH_EXTRA_NAME_NEW_ENCRYPTION, newEnc.name()); + authIntent.putExtra(AUTH_EXTRA_NAME_MESSAGE, R.string.auth_msg_confirm_encryption); + startActivityForResult(authIntent, INTENT_INTERNAL_AUTHENTICATE); + } + + private boolean tryEncryptionChange(EncryptionType newEnc, byte[] newKey) { + Toast upgrading = Toast.makeText(this, R.string.settings_toast_encryption_changing, Toast.LENGTH_LONG); + upgrading.show(); + + if (DatabaseHelper.backupDatabase(this)) { + ArrayList entries = DatabaseHelper.loadDatabase(this, encryptionKey); + + SecretKey newEncryptionKey; + + if (newEnc == EncryptionType.KEYSTORE) { + newEncryptionKey = KeyStoreHelper.loadEncryptionKeyFromKeyStore(this); + } else if (newKey != null && newKey.length > 0) { + newEncryptionKey = EncryptionHelper.generateSymmetricKey(newKey); + } else { + upgrading.cancel(); + DatabaseHelper.restoreDatabaseBackup(this); + return false; + } + + if (DatabaseHelper.saveDatabase(this, entries, newEncryptionKey)) { + encryptionKey = newEncryptionKey; + encryptionChanged = true; + + fragment.encryption.setValue(newEnc.name().toLowerCase()); + + upgrading.cancel(); + Toast.makeText(this, R.string.settings_toast_encryption_change_success, Toast.LENGTH_LONG).show(); + + return true; + } + + DatabaseHelper.restoreDatabaseBackup(this); + + upgrading.cancel(); + Toast.makeText(this, R.string.settings_toast_encryption_change_failed, Toast.LENGTH_LONG).show(); + } else { + upgrading.cancel(); + Toast.makeText(this, R.string.settings_toast_encryption_backup_failed, Toast.LENGTH_LONG).show(); + } + + return false; + } + @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); - if (fragment.pgpKey.handleOnActivityResult(requestCode, resultCode, data)) { + + if (requestCode == INTENT_INTERNAL_AUTHENTICATE) { + if (resultCode == RESULT_OK) { + byte[] authKey = data.getByteArrayExtra(AUTH_EXTRA_NAME_PASSWORD_KEY); + String newEnc = data.getStringExtra(AUTH_EXTRA_NAME_NEW_ENCRYPTION); + + if (authKey != null && authKey.length > 0 && newEnc != null && !newEnc.isEmpty()) { + EncryptionType newEncType = EncryptionType.valueOf(newEnc); + tryEncryptionChange(newEncType, authKey); + } else { + Toast.makeText(this, R.string.settings_toast_encryption_no_key, Toast.LENGTH_LONG).show(); + } + } else { + Toast.makeText(this, R.string.settings_toast_encryption_auth_failed, Toast.LENGTH_LONG).show(); + } + } else if (fragment.pgpKey.handleOnActivityResult(requestCode, resultCode, data)) { // handled by OpenPgpKeyPreference return; } @@ -134,10 +216,10 @@ public class SettingsActivity extends BaseActivity addPreferencesFromResource(R.xml.preferences); CredentialsPreference credentialsPreference = (CredentialsPreference) findPreference(getString(R.string.settings_key_auth)); - credentialsPreference.setEncryptionChangeHandler(new CredentialsPreference.EncryptionChangeHandler() { + credentialsPreference.setEncryptionChangeCallback(new CredentialsPreference.EncryptionChangeCallback() { @Override - public void onEncryptionChanged(byte[] newKey) { - ((SettingsActivity) getActivity()).finishWithResult(true, newKey); + public boolean testEncryptionChange(byte[] newKey) { + return ((SettingsActivity) getActivity()).tryEncryptionChange(settings.getEncryption(), newKey); } }); @@ -160,16 +242,15 @@ public class SettingsActivity extends BaseActivity if (settings.getAuthCredentials(authMethod).isEmpty()) { Toast.makeText(getActivity(), R.string.settings_toast_encryption_invalid_without_credentials, Toast.LENGTH_LONG).show(); return false; - } else { - KeyStoreHelper.wipeKeys(preference.getContext()); } } + + ((SettingsActivity) getActivity()).tryEncryptionChangeWithAuth(encryptionType); + } else if (encryptionType == EncryptionType.KEYSTORE) { + ((SettingsActivity) getActivity()).tryEncryptionChange(encryptionType, null); } - encryption.setValue(newEncryption); - ((SettingsActivity) getActivity()).finishWithResult(true, null); - - return true; + return false; } }); diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Preferences/CredentialsPreference.java b/app/src/main/java/org/shadowice/flocke/andotp/Preferences/CredentialsPreference.java index e2f149b2..ac16fccc 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Preferences/CredentialsPreference.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Preferences/CredentialsPreference.java @@ -25,7 +25,6 @@ package org.shadowice.flocke.andotp.Preferences; import android.app.AlertDialog; import android.app.KeyguardManager; import android.content.Context; -import android.content.res.TypedArray; import android.preference.DialogPreference; import android.support.design.widget.TextInputEditText; import android.support.design.widget.TextInputLayout; @@ -34,7 +33,6 @@ import android.text.InputType; import android.text.TextWatcher; import android.text.method.PasswordTransformationMethod; import android.util.AttributeSet; -import android.util.Base64; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -45,15 +43,11 @@ import android.widget.ListView; import android.widget.Toast; import org.shadowice.flocke.andotp.R; -import org.shadowice.flocke.andotp.Utilities.EncryptionHelper; import org.shadowice.flocke.andotp.Utilities.Settings; -import java.security.NoSuchAlgorithmException; -import java.security.spec.InvalidKeySpecException; import java.util.Arrays; import java.util.List; -import static android.content.Context.AUDIO_SERVICE; import static android.content.Context.KEYGUARD_SERVICE; import static org.shadowice.flocke.andotp.Utilities.Constants.AuthMethod; import static org.shadowice.flocke.andotp.Utilities.Constants.EncryptionType; @@ -62,8 +56,8 @@ public class CredentialsPreference extends DialogPreference implements AdapterView.OnItemClickListener, View.OnClickListener, TextWatcher { public static final AuthMethod DEFAULT_VALUE = AuthMethod.NONE; - public interface EncryptionChangeHandler { - void onEncryptionChanged(byte[] newKey); + public interface EncryptionChangeCallback { + boolean testEncryptionChange(byte[] newKey); } private List entries; @@ -76,7 +70,7 @@ public class CredentialsPreference extends DialogPreference private Settings settings; private AuthMethod value = AuthMethod.NONE; - private EncryptionChangeHandler handler = null; + private EncryptionChangeCallback encryptionChangeCallback = null; private LinearLayout credentialsLayout; private TextInputLayout passwordLayout; @@ -94,8 +88,8 @@ public class CredentialsPreference extends DialogPreference setDialogLayoutResource(R.layout.component_authentication); } - public void setEncryptionChangeHandler(EncryptionChangeHandler handler) { - this.handler = handler; + public void setEncryptionChangeCallback(EncryptionChangeCallback cb) { + this.encryptionChangeCallback = cb; } @Override @@ -184,11 +178,16 @@ public class CredentialsPreference extends DialogPreference } } + if (settings.getEncryption() == EncryptionType.PASSWORD) { + if (newKey == null || encryptionChangeCallback == null) + return; + + if (! encryptionChangeCallback.testEncryptionChange(newKey)) + return; + } + persistString(value.toString().toLowerCase()); setSummary(entries.get(entryValues.indexOf(value))); - - if (settings.getEncryption() == EncryptionType.PASSWORD && newKey != null && handler != null) - handler.onEncryptionChanged(newKey); } @Override diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/DatabaseHelper.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/DatabaseHelper.java index 58d1998a..53ff5562 100644 --- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/DatabaseHelper.java +++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/DatabaseHelper.java @@ -31,18 +31,63 @@ import org.shadowice.flocke.andotp.Database.Entry; import org.shadowice.flocke.andotp.R; import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; import java.util.ArrayList; import javax.crypto.SecretKey; public class DatabaseHelper { public static final String SETTINGS_FILE = "secrets.dat"; + public static final String SETTINGS_FILE_BACKUP = "secrets.dat.bck"; public static void wipeDatabase(Context context) { File db = new File(context.getFilesDir() + "/" + SETTINGS_FILE); db.delete(); } + private static void copyFile(File src, File dst) + throws IOException { + try (InputStream in = new FileInputStream(src)) { + try (OutputStream out = new FileOutputStream(dst)) { + byte[] buffer = new byte[1024]; + int len; + while ((len = in.read(buffer)) > 0) { + out.write(buffer, 0, len); + } + } + } + } + + public static boolean backupDatabase(Context context) { + File original = new File(context.getFilesDir() + "/" + SETTINGS_FILE); + File backup = new File(context.getFilesDir() + "/" + SETTINGS_FILE_BACKUP); + + try { + copyFile(original, backup); + } catch (IOException e) { + return false; + } + + return true; + } + + public static boolean restoreDatabaseBackup(Context context) { + File original = new File(context.getFilesDir() + "/" + SETTINGS_FILE); + File backup = new File(context.getFilesDir() + "/" + SETTINGS_FILE_BACKUP); + + try { + copyFile(backup, original); + } catch (IOException e) { + return false; + } + + return true; + } + /* Database functions */ public static boolean saveDatabase(Context context, ArrayList entries, SecretKey encryptionKey) { if (encryptionKey == null) { diff --git a/app/src/main/res/values/strings_settings.xml b/app/src/main/res/values/strings_settings.xml index e4480895..60cfee12 100644 --- a/app/src/main/res/values/strings_settings.xml +++ b/app/src/main/res/values/strings_settings.xml @@ -75,6 +75,17 @@ You first need to set a Password or PIN before changing the encryption! + Trying to change the database encryption, + please wait! + Successfully changed the database + encryption! + Failed to change database encryption, + restored database from internal backup! + Failed to create an internal + backup, aborting! + Failed to get the encryption key, aborting! + Authentication failed, aborting! + Failed to silently upgrade your password / PIN to the new encryption, please manually reset it in the settings!