mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-19 08:20:20 +00:00
Add support for turning encryption on/off
This commit is contained in:
parent
2400977629
commit
cd781d3236
10 changed files with 123 additions and 31 deletions
|
@ -23,21 +23,17 @@ public class DatabaseFile {
|
||||||
private CryptParameters _cryptParameters;
|
private CryptParameters _cryptParameters;
|
||||||
private SlotCollection _slots;
|
private SlotCollection _slots;
|
||||||
|
|
||||||
public DatabaseFile() {
|
|
||||||
_slots = new SlotCollection();
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] serialize() throws DatabaseFileException {
|
public byte[] serialize() throws DatabaseFileException {
|
||||||
try {
|
try {
|
||||||
JSONObject cryptObj = null;
|
JSONObject cryptObj = null;
|
||||||
if (_cryptParameters != null) {
|
if (isEncrypted()) {
|
||||||
cryptObj = new JSONObject();
|
cryptObj = new JSONObject();
|
||||||
cryptObj.put("nonce", Hex.encode(_cryptParameters.Nonce));
|
cryptObj.put("nonce", Hex.encode(_cryptParameters.Nonce));
|
||||||
cryptObj.put("tag", Hex.encode(_cryptParameters.Tag));
|
cryptObj.put("tag", Hex.encode(_cryptParameters.Tag));
|
||||||
}
|
}
|
||||||
|
|
||||||
// don't write the crypt parameters if the content is not encrypted
|
// 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();
|
JSONObject headerObj = new JSONObject();
|
||||||
headerObj.put("slots", plain ? JSONObject.NULL : SlotCollection.serialize(_slots));
|
headerObj.put("slots", plain ? JSONObject.NULL : SlotCollection.serialize(_slots));
|
||||||
headerObj.put("params", plain ? JSONObject.NULL : cryptObj);
|
headerObj.put("params", plain ? JSONObject.NULL : cryptObj);
|
||||||
|
@ -86,7 +82,7 @@ public class DatabaseFile {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEncrypted() {
|
public boolean isEncrypted() {
|
||||||
return !_slots.isEmpty() && _cryptParameters != null;
|
return _slots != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public JSONObject getContent() {
|
public JSONObject getContent() {
|
||||||
|
@ -106,6 +102,7 @@ public class DatabaseFile {
|
||||||
public void setContent(JSONObject dbObj) {
|
public void setContent(JSONObject dbObj) {
|
||||||
_content = dbObj;
|
_content = dbObj;
|
||||||
_cryptParameters = null;
|
_cryptParameters = null;
|
||||||
|
_slots = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContent(JSONObject dbObj, MasterKey key) throws DatabaseFileException {
|
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.BuildConfig;
|
||||||
import me.impy.aegis.crypto.MasterKey;
|
import me.impy.aegis.crypto.MasterKey;
|
||||||
|
import me.impy.aegis.db.slots.SlotCollection;
|
||||||
|
|
||||||
public class DatabaseManager {
|
public class DatabaseManager {
|
||||||
private static final String FILENAME = "aegis.json";
|
private static final String FILENAME = "aegis.json";
|
||||||
|
@ -195,6 +196,18 @@ public class DatabaseManager {
|
||||||
return _file;
|
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() {
|
public boolean isLoaded() {
|
||||||
return _file != null;
|
return _file != null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,10 +89,6 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
return _slots.size();
|
return _slots.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isEmpty() {
|
|
||||||
return _slots.size() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T extends Slot> T find(Class<T> type) {
|
public <T extends Slot> T find(Class<T> type) {
|
||||||
for (Slot slot : this) {
|
for (Slot slot : this) {
|
||||||
if (slot.getClass() == type) {
|
if (slot.getClass() == type) {
|
||||||
|
|
|
@ -327,6 +327,7 @@ public class MainActivity extends AegisActivity implements KeyProfileView.Listen
|
||||||
@Override
|
@Override
|
||||||
protected void onResume() {
|
protected void onResume() {
|
||||||
super.onResume();
|
super.onResume();
|
||||||
|
updateLockIcon();
|
||||||
|
|
||||||
// refresh all codes to prevent showing old ones
|
// refresh all codes to prevent showing old ones
|
||||||
_keyProfileView.refresh();
|
_keyProfileView.refresh();
|
||||||
|
|
|
@ -2,7 +2,12 @@ package me.impy.aegis.ui;
|
||||||
|
|
||||||
import android.os.Bundle;
|
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;
|
private PreferencesFragment _fragment;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -36,4 +41,14 @@ public class PreferencesActivity extends AegisActivity {
|
||||||
outState.putParcelable("result", _fragment.getResult());
|
outState.putParcelable("result", _fragment.getResult());
|
||||||
super.onSaveInstanceState(outState);
|
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.EditTextPreference;
|
||||||
import android.preference.Preference;
|
import android.preference.Preference;
|
||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
|
import android.preference.SwitchPreference;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.view.WindowManager;
|
import android.view.WindowManager;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -22,20 +24,25 @@ import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
import me.impy.aegis.AegisApplication;
|
import me.impy.aegis.AegisApplication;
|
||||||
import me.impy.aegis.R;
|
import me.impy.aegis.R;
|
||||||
import me.impy.aegis.crypto.MasterKey;
|
import me.impy.aegis.crypto.MasterKey;
|
||||||
import me.impy.aegis.db.DatabaseEntry;
|
import me.impy.aegis.db.DatabaseEntry;
|
||||||
import me.impy.aegis.db.DatabaseManager;
|
import me.impy.aegis.db.DatabaseManager;
|
||||||
import me.impy.aegis.db.DatabaseManagerException;
|
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.SlotCollection;
|
||||||
|
import me.impy.aegis.db.slots.SlotException;
|
||||||
import me.impy.aegis.helpers.PermissionHelper;
|
import me.impy.aegis.helpers.PermissionHelper;
|
||||||
import me.impy.aegis.importers.AegisImporter;
|
import me.impy.aegis.importers.AegisImporter;
|
||||||
import me.impy.aegis.importers.DatabaseImporter;
|
import me.impy.aegis.importers.DatabaseImporter;
|
||||||
import me.impy.aegis.importers.DatabaseImporterException;
|
import me.impy.aegis.importers.DatabaseImporterException;
|
||||||
|
import me.impy.aegis.ui.dialogs.PasswordDialogFragment;
|
||||||
import me.impy.aegis.util.ByteInputStream;
|
import me.impy.aegis.util.ByteInputStream;
|
||||||
|
|
||||||
public class PreferencesFragment extends PreferenceFragment {
|
public class PreferencesFragment extends PreferenceFragment implements PasswordDialogFragment.Listener {
|
||||||
// activity request codes
|
// activity request codes
|
||||||
private static final int CODE_IMPORT = 0;
|
private static final int CODE_IMPORT = 0;
|
||||||
private static final int CODE_IMPORT_DECRYPT = 1;
|
private static final int CODE_IMPORT_DECRYPT = 1;
|
||||||
|
@ -53,6 +60,9 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
private DatabaseImporter _importer;
|
private DatabaseImporter _importer;
|
||||||
private Class<? extends DatabaseImporter> _importerType;
|
private Class<? extends DatabaseImporter> _importerType;
|
||||||
|
|
||||||
|
private SwitchPreference _encryptionPreference;
|
||||||
|
private Preference _slotsPreference;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(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");
|
EditTextPreference timeoutPreference = (EditTextPreference) findPreference("pref_timeout");
|
||||||
timeoutPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
timeoutPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -139,6 +135,45 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
return true;
|
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
|
@Override
|
||||||
|
@ -195,8 +230,6 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
.setSingleChoiceItems(names, 0, null)
|
.setSingleChoiceItems(names, 0, null)
|
||||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int which) {
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
dialog.dismiss();
|
|
||||||
|
|
||||||
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
int i = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
|
||||||
_importerType = importers.get(names[i]);
|
_importerType = importers.get(names[i]);
|
||||||
|
|
||||||
|
@ -353,4 +386,34 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
|
|
||||||
return true;
|
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.app.Dialog;
|
||||||
import android.hardware.fingerprint.FingerprintManager;
|
import android.hardware.fingerprint.FingerprintManager;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ public class PasswordDialogFragment extends SlotDialogFragment {
|
||||||
|
|
||||||
char[] password = EditTextHelper.getEditTextChars(textPassword);
|
char[] password = EditTextHelper.getEditTextChars(textPassword);
|
||||||
PasswordSlot slot = new PasswordSlot();
|
PasswordSlot slot = new PasswordSlot();
|
||||||
DerivationTask task = new DerivationTask(getContext(), key -> {
|
DerivationTask task = new DerivationTask(getActivity(), key -> {
|
||||||
Cipher cipher;
|
Cipher cipher;
|
||||||
try {
|
try {
|
||||||
cipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
|
cipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
|
||||||
|
|
|
@ -22,6 +22,8 @@
|
||||||
<string name="pref_export_summary">Export the database</string>
|
<string name="pref_export_summary">Export the database</string>
|
||||||
<string name="pref_secure_screen_title">Screen security</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_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_hint">Touch sensor</string>
|
||||||
<string name="fingerprint_not_recognized">Fingerprint not recognized. Try again</string>
|
<string name="fingerprint_not_recognized">Fingerprint not recognized. Try again</string>
|
||||||
|
|
|
@ -31,6 +31,11 @@
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:defaultValue="30"
|
android:defaultValue="30"
|
||||||
android:dialogTitle="Set number of seconds of inactivity before Aegis locks the database"/>
|
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
|
<Preference
|
||||||
android:key="pref_slots"
|
android:key="pref_slots"
|
||||||
android:title="@string/pref_slots_title"
|
android:title="@string/pref_slots_title"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue