Add support for turning encryption on/off

pull/41/head
Alexander Bakker 7 years ago
parent 2400977629
commit cd781d3236

@ -23,21 +23,17 @@ public class DatabaseFile {
private CryptParameters _cryptParameters;
private SlotCollection _slots;
public DatabaseFile() {
_slots = new SlotCollection();
}
public byte[] serialize() throws DatabaseFileException {
try {
JSONObject cryptObj = null;
if (_cryptParameters != null) {
if (isEncrypted()) {
cryptObj = new JSONObject();
cryptObj.put("nonce", Hex.encode(_cryptParameters.Nonce));
cryptObj.put("tag", Hex.encode(_cryptParameters.Tag));
}
// don't write the crypt parameters if the content is not encrypted
boolean plain = _content instanceof JSONObject || _slots.isEmpty() || cryptObj == null;
boolean plain = _content instanceof JSONObject || _slots == null || cryptObj == null;
JSONObject headerObj = new JSONObject();
headerObj.put("slots", plain ? JSONObject.NULL : SlotCollection.serialize(_slots));
headerObj.put("params", plain ? JSONObject.NULL : cryptObj);
@ -86,7 +82,7 @@ public class DatabaseFile {
}
public boolean isEncrypted() {
return !_slots.isEmpty() && _cryptParameters != null;
return _slots != null;
}
public JSONObject getContent() {
@ -106,6 +102,7 @@ public class DatabaseFile {
public void setContent(JSONObject dbObj) {
_content = dbObj;
_cryptParameters = null;
_slots = null;
}
public void setContent(JSONObject dbObj, MasterKey key) throws DatabaseFileException {

@ -14,6 +14,7 @@ import java.util.List;
import me.impy.aegis.BuildConfig;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.db.slots.SlotCollection;
public class DatabaseManager {
private static final String FILENAME = "aegis.json";
@ -195,6 +196,18 @@ public class DatabaseManager {
return _file;
}
public void enableEncryption(MasterKey key, SlotCollection slots) {
assertState(false, true);
_key = key;
_file.setSlots(slots);
}
public void disableEncryption() {
assertState(false, true);
_key = null;
_file.setSlots(null);
}
public boolean isLoaded() {
return _file != null;
}

@ -89,10 +89,6 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
return _slots.size();
}
public boolean isEmpty() {
return _slots.size() == 0;
}
public <T extends Slot> T find(Class<T> type) {
for (Slot slot : this) {
if (slot.getClass() == type) {

@ -327,6 +327,7 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen
@Override
protected void onResume() {
super.onResume();
updateLockIcon();
// refresh all codes to prevent showing old ones
_keyProfileView.refresh();

@ -2,7 +2,12 @@ package me.impy.aegis.ui;
import android.os.Bundle;
public class PreferencesActivity extends AegisActivity {
import javax.crypto.Cipher;
import me.impy.aegis.db.slots.Slot;
import me.impy.aegis.ui.dialogs.PasswordDialogFragment;
public class PreferencesActivity extends AegisActivity implements PasswordDialogFragment.Listener {
private PreferencesFragment _fragment;
@Override
@ -36,4 +41,14 @@ public class PreferencesActivity extends AegisActivity {
outState.putParcelable("result", _fragment.getResult());
super.onSaveInstanceState(outState);
}
@Override
public void onSlotResult(Slot slot, Cipher cipher) {
_fragment.onSlotResult(slot, cipher);
}
@Override
public void onException(Exception e) {
_fragment.onException(e);
}
}

@ -10,7 +10,9 @@ import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceFragment;
import android.preference.SwitchPreference;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Toast;
@ -22,20 +24,25 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import javax.crypto.Cipher;
import me.impy.aegis.AegisApplication;
import me.impy.aegis.R;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.db.DatabaseEntry;
import me.impy.aegis.db.DatabaseManager;
import me.impy.aegis.db.DatabaseManagerException;
import me.impy.aegis.db.slots.Slot;
import me.impy.aegis.db.slots.SlotCollection;
import me.impy.aegis.db.slots.SlotException;
import me.impy.aegis.helpers.PermissionHelper;
import me.impy.aegis.importers.AegisImporter;
import me.impy.aegis.importers.DatabaseImporter;
import me.impy.aegis.importers.DatabaseImporterException;
import me.impy.aegis.ui.dialogs.PasswordDialogFragment;
import me.impy.aegis.util.ByteInputStream;
public class PreferencesFragment extends PreferenceFragment {
public class PreferencesFragment extends PreferenceFragment implements PasswordDialogFragment.Listener {
// activity request codes
private static final int CODE_IMPORT = 0;
private static final int CODE_IMPORT_DECRYPT = 1;
@ -53,6 +60,9 @@ public class PreferencesFragment extends PreferenceFragment {
private DatabaseImporter _importer;
private Class<? extends DatabaseImporter> _importerType;
private SwitchPreference _encryptionPreference;
private Preference _slotsPreference;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@ -92,20 +102,6 @@ public class PreferencesFragment extends PreferenceFragment {
}
});
Preference slotsPreference = findPreference("pref_slots");
slotsPreference.setEnabled(_db.getFile().isEncrypted());
slotsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
MasterKey masterKey = _db.getMasterKey();
Intent intent = new Intent(getActivity(), SlotManagerActivity.class);
intent.putExtra("masterKey", masterKey);
intent.putExtra("slots", _db.getFile().getSlots());
startActivityForResult(intent, CODE_SLOTS);
return true;
}
});
EditTextPreference timeoutPreference = (EditTextPreference) findPreference("pref_timeout");
timeoutPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
@ -139,6 +135,45 @@ public class PreferencesFragment extends PreferenceFragment {
return true;
}
});
_encryptionPreference = (SwitchPreference) findPreference("pref_encryption");
_encryptionPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (!_db.getFile().isEncrypted()) {
PasswordDialogFragment dialog = new PasswordDialogFragment();
// TODO: find a less ugly way to obtain the fragment manager
dialog.show(((AppCompatActivity)getActivity()).getSupportFragmentManager(), null);
} else {
new AlertDialog.Builder(getActivity())
.setTitle("Disable encryption")
.setMessage("Are you sure you want to disable encryption? This will cause the database to be stored in plain text")
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
_db.disableEncryption();
saveDatabase();
updateEncryptionPreference();
}
})
.setNegativeButton(android.R.string.no, null)
.show();
}
return false;
}
});
_slotsPreference = findPreference("pref_slots");
_slotsPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
MasterKey masterKey = _db.getMasterKey();
Intent intent = new Intent(getActivity(), SlotManagerActivity.class);
intent.putExtra("masterKey", masterKey);
intent.putExtra("slots", _db.getFile().getSlots());
startActivityForResult(intent, CODE_SLOTS);
return true;
}
});
updateEncryptionPreference();
}
@Override
@ -195,8 +230,6 @@ public class PreferencesFragment extends PreferenceFragment {
.setSingleChoiceItems(names, 0, null)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
_importerType = importers.get(names[i]);
@ -353,4 +386,34 @@ public class PreferencesFragment extends PreferenceFragment {
return true;
}
@Override
public void onSlotResult(Slot slot, Cipher cipher) {
MasterKey masterKey = MasterKey.generate();
SlotCollection slots = new SlotCollection();
try {
slots.encrypt(slot, masterKey, cipher);
} catch (SlotException e) {
onException(e);
return;
}
slots.add(slot);
_db.enableEncryption(masterKey, slots);
saveDatabase();
updateEncryptionPreference();
}
@Override
public void onException(Exception e) {
updateEncryptionPreference();
Toast.makeText(getActivity(), "An error occurred while trying to set the password: " + e.getMessage(), Toast.LENGTH_SHORT).show();
}
private void updateEncryptionPreference() {
boolean encrypted = _db.getFile().isEncrypted();
_encryptionPreference.setChecked(encrypted);
_slotsPreference.setEnabled(encrypted);
}
}

@ -3,8 +3,8 @@ package me.impy.aegis.ui.dialogs;
import android.app.Dialog;
import android.hardware.fingerprint.FingerprintManager;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.app.AlertDialog;
import android.support.annotation.NonNull;
import android.view.View;
import android.widget.TextView;

@ -48,7 +48,7 @@ public class PasswordDialogFragment extends SlotDialogFragment {
char[] password = EditTextHelper.getEditTextChars(textPassword);
PasswordSlot slot = new PasswordSlot();
DerivationTask task = new DerivationTask(getContext(), key -> {
DerivationTask task = new DerivationTask(getActivity(), key -> {
Cipher cipher;
try {
cipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);

@ -22,6 +22,8 @@
<string name="pref_export_summary">Export the database</string>
<string name="pref_secure_screen_title">Screen security</string>
<string name="pref_secure_screen_summary">Block screenshots and other attempts to capture the screen within the app</string>
<string name="pref_encryption_title">Encryption</string>
<string name="pref_encryption_summary">Encrypt the database and unlock it with a password or fingerprint</string>
<string name="fingerprint_hint">Touch sensor</string>
<string name="fingerprint_not_recognized">Fingerprint not recognized. Try again</string>

@ -31,6 +31,11 @@
android:inputType="number"
android:defaultValue="30"
android:dialogTitle="Set number of seconds of inactivity before Aegis locks the database"/>
<SwitchPreference
android:key="pref_encryption"
android:title="@string/pref_encryption_title"
android:summary="@string/pref_encryption_summary"
android:persistent="false"/>
<Preference
android:key="pref_slots"
android:title="@string/pref_slots_title"

Loading…
Cancel
Save