mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-15 14:32:49 +00:00
parent
7f09eb5535
commit
6770ccd3b1
5 changed files with 141 additions and 47 deletions
|
@ -1,7 +1,10 @@
|
||||||
package me.impy.aegis.importers;
|
package me.impy.aegis.importers;
|
||||||
|
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import me.impy.aegis.crypto.MasterKey;
|
||||||
import me.impy.aegis.db.Database;
|
import me.impy.aegis.db.Database;
|
||||||
import me.impy.aegis.db.DatabaseEntry;
|
import me.impy.aegis.db.DatabaseEntry;
|
||||||
import me.impy.aegis.db.DatabaseException;
|
import me.impy.aegis.db.DatabaseException;
|
||||||
|
@ -10,25 +13,55 @@ import me.impy.aegis.db.DatabaseFileException;
|
||||||
import me.impy.aegis.util.ByteInputStream;
|
import me.impy.aegis.util.ByteInputStream;
|
||||||
|
|
||||||
public class AegisImporter extends DatabaseImporter {
|
public class AegisImporter extends DatabaseImporter {
|
||||||
|
private MasterKey _key;
|
||||||
|
private DatabaseFile _file;
|
||||||
|
|
||||||
public AegisImporter(ByteInputStream stream) {
|
public AegisImporter(ByteInputStream stream) {
|
||||||
super(stream);
|
super(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DatabaseEntry> convert() throws DatabaseImporterException {
|
public void parse() throws DatabaseImporterException {
|
||||||
try {
|
try {
|
||||||
byte[] bytes = _stream.getBytes();
|
byte[] bytes = _stream.getBytes();
|
||||||
DatabaseFile file = new DatabaseFile();
|
_file = new DatabaseFile();
|
||||||
file.deserialize(bytes);
|
_file.deserialize(bytes);
|
||||||
Database db = new Database();
|
} catch (DatabaseFileException e) {
|
||||||
db.deserialize(file.getContent());
|
|
||||||
return db.getKeys();
|
|
||||||
} catch (DatabaseFileException | DatabaseException e) {
|
|
||||||
throw new DatabaseImporterException(e);
|
throw new DatabaseImporterException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DatabaseEntry> convert() throws DatabaseImporterException {
|
||||||
|
try {
|
||||||
|
JSONObject obj;
|
||||||
|
if (!_file.isEncrypted()) {
|
||||||
|
obj = _file.getContent();
|
||||||
|
} else {
|
||||||
|
obj = _file.getContent(_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
Database db = new Database();
|
||||||
|
db.deserialize(obj);
|
||||||
|
return db.getKeys();
|
||||||
|
} catch (DatabaseException | DatabaseFileException e) {
|
||||||
|
throw new DatabaseImporterException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEncrypted() {
|
||||||
|
return _file.isEncrypted();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKey(MasterKey key) {
|
||||||
|
_key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DatabaseFile getFile() {
|
||||||
|
return _file;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "Aegis";
|
return "Aegis";
|
||||||
|
|
|
@ -20,8 +20,12 @@ public abstract class DatabaseImporter {
|
||||||
_stream = stream;
|
_stream = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void parse() throws DatabaseImporterException;
|
||||||
|
|
||||||
public abstract List<DatabaseEntry> convert() throws DatabaseImporterException;
|
public abstract List<DatabaseEntry> convert() throws DatabaseImporterException;
|
||||||
|
|
||||||
|
public abstract boolean isEncrypted();
|
||||||
|
|
||||||
public abstract String getName();
|
public abstract String getName();
|
||||||
|
|
||||||
public static DatabaseImporter create(ByteInputStream stream, Class<? extends DatabaseImporter> type) {
|
public static DatabaseImporter create(ByteInputStream stream, Class<? extends DatabaseImporter> type) {
|
||||||
|
|
|
@ -27,6 +27,11 @@ public class FreeOTPImporter extends DatabaseImporter {
|
||||||
String Value;
|
String Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void parse() throws DatabaseImporterException {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<DatabaseEntry> convert() throws DatabaseImporterException {
|
public List<DatabaseEntry> convert() throws DatabaseImporterException {
|
||||||
try {
|
try {
|
||||||
|
@ -40,6 +45,11 @@ public class FreeOTPImporter extends DatabaseImporter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEncrypted() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return "FreeOTP";
|
return "FreeOTP";
|
||||||
|
|
|
@ -3,13 +3,13 @@ package me.impy.aegis.ui;
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
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.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -18,10 +18,12 @@ import java.util.Locale;
|
||||||
|
|
||||||
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.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.helpers.PermissionHelper;
|
import me.impy.aegis.helpers.PermissionHelper;
|
||||||
|
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.util.ByteInputStream;
|
import me.impy.aegis.util.ByteInputStream;
|
||||||
|
@ -29,6 +31,7 @@ import me.impy.aegis.util.ByteInputStream;
|
||||||
public class PreferencesFragment extends PreferenceFragment {
|
public class PreferencesFragment extends PreferenceFragment {
|
||||||
// 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;
|
||||||
|
|
||||||
// permission request codes
|
// permission request codes
|
||||||
private static final int CODE_PERM_IMPORT = 0;
|
private static final int CODE_PERM_IMPORT = 0;
|
||||||
|
@ -38,9 +41,12 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
public static final int ACTION_SLOTS = 1;
|
public static final int ACTION_SLOTS = 1;
|
||||||
|
|
||||||
private Intent _result = new Intent();
|
private Intent _result = new Intent();
|
||||||
private AegisApplication _app;
|
|
||||||
private DatabaseManager _db;
|
private DatabaseManager _db;
|
||||||
|
|
||||||
|
// this is used to keep a reference to a database converter
|
||||||
|
// while the user provides credentials to decrypt it
|
||||||
|
private DatabaseImporter _converter;
|
||||||
|
|
||||||
private void setResult() {
|
private void setResult() {
|
||||||
getActivity().setResult(Activity.RESULT_OK, _result);
|
getActivity().setResult(Activity.RESULT_OK, _result);
|
||||||
}
|
}
|
||||||
|
@ -54,8 +60,9 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
public void onCreate(Bundle savedInstanceState) {
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
_app = (AegisApplication) getActivity().getApplication();
|
|
||||||
_db = _app.getDatabaseManager();
|
AegisApplication app = (AegisApplication) getActivity().getApplication();
|
||||||
|
_db = app.getDatabaseManager();
|
||||||
|
|
||||||
// set the result intent in advance
|
// set the result intent in advance
|
||||||
setResult();
|
setResult();
|
||||||
|
@ -145,6 +152,9 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
case CODE_IMPORT:
|
case CODE_IMPORT:
|
||||||
onImportResult(resultCode, data);
|
onImportResult(resultCode, data);
|
||||||
break;
|
break;
|
||||||
|
case CODE_IMPORT_DECRYPT:
|
||||||
|
onImportDecryptResult(resultCode, data);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,44 +164,41 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
startActivityForResult(intent, CODE_IMPORT);
|
startActivityForResult(intent, CODE_IMPORT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onImportResult(int resultCode, Intent data) {
|
private void onImportDecryptResult(int resultCode, Intent data) {
|
||||||
if (resultCode != Activity.RESULT_OK) {
|
if (resultCode != Activity.RESULT_OK) {
|
||||||
|
_converter = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
InputStream fileStream = null;
|
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
||||||
List<DatabaseEntry> entries = null;
|
((AegisImporter)_converter).setKey(key);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
try {
|
importDatabase(_converter);
|
||||||
fileStream = getActivity().getContentResolver().openInputStream(data.getData());
|
} catch (DatabaseImporterException e) {
|
||||||
} catch (FileNotFoundException e) {
|
e.printStackTrace();
|
||||||
Toast.makeText(getActivity(), "Error: File not found", Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), "An error occurred while trying to parse the file", Toast.LENGTH_SHORT).show();
|
||||||
return;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteInputStream stream;
|
private void onImportResult(int resultCode, Intent data) {
|
||||||
try {
|
Uri uri = data.getData();
|
||||||
int read;
|
if (resultCode != Activity.RESULT_OK || uri == null) {
|
||||||
byte[] buf = new byte[4096];
|
return;
|
||||||
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
}
|
||||||
while ((read = fileStream.read(buf, 0, buf.length)) != -1) {
|
|
||||||
outStream.write(buf, 0, read);
|
|
||||||
}
|
|
||||||
stream = new ByteInputStream(outStream.toByteArray());
|
|
||||||
} catch (IOException e) {
|
|
||||||
Toast.makeText(getActivity(), "An error occurred while trying to read the file", Toast.LENGTH_SHORT).show();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (DatabaseImporter converter : DatabaseImporter.create(stream)) {
|
ByteInputStream stream;
|
||||||
try {
|
InputStream fileStream = null;
|
||||||
entries = converter.convert();
|
|
||||||
break;
|
try {
|
||||||
} catch (DatabaseImporterException e) {
|
fileStream = getActivity().getContentResolver().openInputStream(uri);
|
||||||
stream.reset();
|
stream = ByteInputStream.create(fileStream);
|
||||||
}
|
} catch (FileNotFoundException e) {
|
||||||
}
|
Toast.makeText(getActivity(), "Error: File not found", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
|
} catch (IOException e) {
|
||||||
|
Toast.makeText(getActivity(), "An error occurred while trying to read the file", Toast.LENGTH_SHORT).show();
|
||||||
|
return;
|
||||||
} finally {
|
} finally {
|
||||||
if (fileStream != null) {
|
if (fileStream != null) {
|
||||||
try {
|
try {
|
||||||
|
@ -202,11 +209,37 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (entries == null) {
|
boolean imported = false;
|
||||||
Toast.makeText(getActivity(), "An error occurred while trying to parse the file", Toast.LENGTH_SHORT).show();
|
for (DatabaseImporter converter : DatabaseImporter.create(stream)) {
|
||||||
return;
|
try {
|
||||||
|
converter.parse();
|
||||||
|
|
||||||
|
// special case to decrypt encrypted aegis databases
|
||||||
|
if (converter.isEncrypted() && converter instanceof AegisImporter) {
|
||||||
|
_converter = converter;
|
||||||
|
|
||||||
|
Intent intent = new Intent(getActivity(), AuthActivity.class);
|
||||||
|
intent.putExtra("slots", ((AegisImporter)_converter).getFile().getSlots());
|
||||||
|
startActivityForResult(intent, CODE_IMPORT_DECRYPT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
importDatabase(converter);
|
||||||
|
imported = true;
|
||||||
|
break;
|
||||||
|
} catch (DatabaseImporterException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
stream.reset();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!imported) {
|
||||||
|
Toast.makeText(getActivity(), "An error occurred while trying to parse the file", Toast.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void importDatabase(DatabaseImporter converter) throws DatabaseImporterException {
|
||||||
|
List<DatabaseEntry> entries = converter.convert();
|
||||||
for (DatabaseEntry entry : entries) {
|
for (DatabaseEntry entry : entries) {
|
||||||
_db.addKey(entry);
|
_db.addKey(entry);
|
||||||
}
|
}
|
||||||
|
@ -220,6 +253,6 @@ public class PreferencesFragment extends PreferenceFragment {
|
||||||
}
|
}
|
||||||
|
|
||||||
_result.putExtra("needsReload", true);
|
_result.putExtra("needsReload", true);
|
||||||
Toast.makeText(getActivity(), String.format(Locale.getDefault(), "Imported %d entries", entries.size()), Toast.LENGTH_SHORT).show();
|
Toast.makeText(getActivity(), String.format(Locale.getDefault(), "Imported %d entries", entries.size()), Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -1,12 +1,26 @@
|
||||||
package me.impy.aegis.util;
|
package me.impy.aegis.util;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
public class ByteInputStream extends ByteArrayInputStream {
|
public class ByteInputStream extends ByteArrayInputStream {
|
||||||
public ByteInputStream(byte[] buf) {
|
private ByteInputStream(byte[] buf) {
|
||||||
super(buf);
|
super(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ByteInputStream create(InputStream fileStream) throws IOException {
|
||||||
|
int read;
|
||||||
|
byte[] buf = new byte[4096];
|
||||||
|
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
|
||||||
|
while ((read = fileStream.read(buf, 0, buf.length)) != -1) {
|
||||||
|
outStream.write(buf, 0, read);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ByteInputStream(outStream.toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
public byte[] getBytes() {
|
public byte[] getBytes() {
|
||||||
return this.buf;
|
return this.buf;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue