mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-04 20:30:36 +00:00
Refactor the database classes to be more reusable
This commit is contained in:
parent
571cf20eda
commit
0434513820
18 changed files with 311 additions and 256 deletions
|
@ -30,7 +30,7 @@ public class CryptParameters implements Serializable {
|
|||
return obj;
|
||||
}
|
||||
|
||||
public static CryptParameters parseJson(JSONObject obj) throws JSONException, HexException {
|
||||
public static CryptParameters fromJson(JSONObject obj) throws JSONException, HexException {
|
||||
byte[] nonce = Hex.decode(obj.getString("nonce"));
|
||||
byte[] tag = Hex.decode(obj.getString("tag"));
|
||||
return new CryptParameters(nonce, tag);
|
||||
|
|
|
@ -12,14 +12,13 @@ import me.impy.aegis.otp.OtpInfoException;
|
|||
|
||||
public class Database {
|
||||
private static final int VERSION = 1;
|
||||
|
||||
private DatabaseEntryList _entries = new DatabaseEntryList();
|
||||
|
||||
public JSONObject serialize() {
|
||||
public JSONObject toJson() {
|
||||
try {
|
||||
JSONArray array = new JSONArray();
|
||||
for (DatabaseEntry e : _entries) {
|
||||
array.put(e.serialize());
|
||||
array.put(e.toJson());
|
||||
}
|
||||
|
||||
JSONObject obj = new JSONObject();
|
||||
|
@ -31,8 +30,9 @@ public class Database {
|
|||
}
|
||||
}
|
||||
|
||||
public void deserialize(JSONObject obj) throws DatabaseException {
|
||||
// TODO: support different VERSION deserialization providers
|
||||
public static Database fromJson(JSONObject obj) throws DatabaseException {
|
||||
Database db = new Database();
|
||||
|
||||
try {
|
||||
int ver = obj.getInt("version");
|
||||
if (ver != VERSION) {
|
||||
|
@ -41,13 +41,14 @@ public class Database {
|
|||
|
||||
JSONArray array = obj.getJSONArray("entries");
|
||||
for (int i = 0; i < array.length(); i++) {
|
||||
DatabaseEntry entry = new DatabaseEntry(null);
|
||||
entry.deserialize(array.getJSONObject(i));
|
||||
addEntry(entry);
|
||||
DatabaseEntry entry = DatabaseEntry.fromJson(array.getJSONObject(i));
|
||||
db.addEntry(entry);
|
||||
}
|
||||
} catch (Base64Exception | OtpInfoException | JSONException e) {
|
||||
throw new DatabaseException(e);
|
||||
}
|
||||
|
||||
return db;
|
||||
}
|
||||
|
||||
public void addEntry(DatabaseEntry entry) {
|
||||
|
|
|
@ -9,6 +9,7 @@ import java.util.UUID;
|
|||
|
||||
import me.impy.aegis.encoding.Base64;
|
||||
import me.impy.aegis.encoding.Base64Exception;
|
||||
import me.impy.aegis.otp.GoogleAuthInfo;
|
||||
import me.impy.aegis.otp.OtpInfo;
|
||||
import me.impy.aegis.otp.OtpInfoException;
|
||||
|
||||
|
@ -19,9 +20,13 @@ public class DatabaseEntry implements Serializable {
|
|||
private OtpInfo _info;
|
||||
private byte[] _icon;
|
||||
|
||||
public DatabaseEntry(OtpInfo info) {
|
||||
private DatabaseEntry(UUID uuid, OtpInfo info) {
|
||||
_uuid = uuid;
|
||||
_info = info;
|
||||
_uuid = UUID.randomUUID();
|
||||
}
|
||||
|
||||
public DatabaseEntry(OtpInfo info) {
|
||||
this(UUID.randomUUID(), info);
|
||||
}
|
||||
|
||||
public DatabaseEntry(OtpInfo info, String name, String issuer) {
|
||||
|
@ -30,7 +35,11 @@ public class DatabaseEntry implements Serializable {
|
|||
setIssuer(issuer);
|
||||
}
|
||||
|
||||
public JSONObject serialize() {
|
||||
public DatabaseEntry(GoogleAuthInfo info) {
|
||||
this(info.getOtpInfo(), info.getAccountName(), info.getIssuer());
|
||||
}
|
||||
|
||||
public JSONObject toJson() {
|
||||
JSONObject obj = new JSONObject();
|
||||
|
||||
try {
|
||||
|
@ -47,22 +56,26 @@ public class DatabaseEntry implements Serializable {
|
|||
return obj;
|
||||
}
|
||||
|
||||
public void deserialize(JSONObject obj) throws JSONException, OtpInfoException, Base64Exception {
|
||||
public static DatabaseEntry fromJson(JSONObject obj) throws JSONException, OtpInfoException, Base64Exception {
|
||||
// if there is no uuid, generate a new one
|
||||
UUID uuid;
|
||||
if (!obj.has("uuid")) {
|
||||
_uuid = UUID.randomUUID();
|
||||
uuid = UUID.randomUUID();
|
||||
} else {
|
||||
_uuid = UUID.fromString(obj.getString("uuid"));
|
||||
uuid = UUID.fromString(obj.getString("uuid"));
|
||||
}
|
||||
_name = obj.getString("name");
|
||||
_issuer = obj.getString("issuer");
|
||||
|
||||
OtpInfo info = OtpInfo.fromJson(obj.getString("type"), obj.getJSONObject("info"));
|
||||
DatabaseEntry entry = new DatabaseEntry(uuid, info);
|
||||
entry.setName(obj.getString("name"));
|
||||
entry.setIssuer(obj.getString("issuer"));
|
||||
|
||||
Object icon = obj.get("icon");
|
||||
if (icon != JSONObject.NULL) {
|
||||
_icon = Base64.decode((String) icon);
|
||||
entry.setIcon(Base64.decode((String) icon));
|
||||
}
|
||||
|
||||
_info = OtpInfo.parseJson(obj.getString("type"), obj.getJSONObject("info"));
|
||||
return entry;
|
||||
}
|
||||
|
||||
public void resetUUID() {
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package me.impy.aegis.db;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
|
@ -8,7 +7,6 @@ import java.io.UnsupportedEncodingException;
|
|||
|
||||
import me.impy.aegis.crypto.CryptParameters;
|
||||
import me.impy.aegis.crypto.CryptResult;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.crypto.MasterKeyException;
|
||||
import me.impy.aegis.db.slots.SlotList;
|
||||
import me.impy.aegis.db.slots.SlotListException;
|
||||
|
@ -20,22 +18,41 @@ public class DatabaseFile {
|
|||
public static final byte VERSION = 1;
|
||||
|
||||
private Object _content;
|
||||
private CryptParameters _cryptParameters;
|
||||
private SlotList _slots;
|
||||
private Header _header;
|
||||
|
||||
public byte[] serialize() {
|
||||
public DatabaseFile() {
|
||||
|
||||
}
|
||||
|
||||
private DatabaseFile(Object content, Header header) {
|
||||
_content = content;
|
||||
_header = header;
|
||||
}
|
||||
|
||||
public Header getHeader() {
|
||||
return _header;
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return !_header.isEmpty();
|
||||
}
|
||||
|
||||
public JSONObject toJson() {
|
||||
try {
|
||||
// don't write the crypt parameters and slots if the content is not encrypted
|
||||
boolean plain = _content instanceof JSONObject || !isEncrypted();
|
||||
JSONObject headerObj = new JSONObject();
|
||||
headerObj.put("slots", plain ? JSONObject.NULL : SlotList.serialize(_slots));
|
||||
headerObj.put("params", plain ? JSONObject.NULL : _cryptParameters.toJson());
|
||||
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("version", VERSION);
|
||||
obj.put("header", headerObj);
|
||||
obj.put("header", _header.toJson());
|
||||
obj.put("db", _content);
|
||||
return obj;
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
JSONObject obj = toJson();
|
||||
|
||||
try {
|
||||
String string = obj.toString(4);
|
||||
return string.getBytes("UTF-8");
|
||||
} catch (JSONException | UnsupportedEncodingException e) {
|
||||
|
@ -43,76 +60,108 @@ public class DatabaseFile {
|
|||
}
|
||||
}
|
||||
|
||||
public void deserialize(byte[] data) throws DatabaseFileException {
|
||||
public static DatabaseFile fromJson(JSONObject obj) throws DatabaseFileException {
|
||||
try {
|
||||
JSONObject obj = new JSONObject(new String(data, "UTF-8"));
|
||||
JSONObject headerObj = obj.getJSONObject("header");
|
||||
if (obj.getInt("version") > VERSION) {
|
||||
throw new DatabaseFileException("unsupported version");
|
||||
}
|
||||
|
||||
JSONArray slotObj = headerObj.optJSONArray("slots");
|
||||
if (slotObj != null) {
|
||||
_slots = SlotList.deserialize(slotObj);
|
||||
Header header = Header.fromJson(obj.getJSONObject("header"));
|
||||
if (!header.isEmpty()) {
|
||||
return new DatabaseFile(obj.getString("db"), header);
|
||||
}
|
||||
|
||||
JSONObject cryptObj = headerObj.optJSONObject("params");
|
||||
if (cryptObj != null) {
|
||||
_cryptParameters = CryptParameters.parseJson(cryptObj);
|
||||
}
|
||||
|
||||
if (cryptObj == null || slotObj == null) {
|
||||
_content = obj.getJSONObject("db");
|
||||
} else {
|
||||
_content = obj.getString("db");
|
||||
}
|
||||
} catch (SlotListException | UnsupportedEncodingException | JSONException | HexException e) {
|
||||
return new DatabaseFile(obj.getJSONObject("db"), header);
|
||||
} catch (JSONException e) {
|
||||
throw new DatabaseFileException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isEncrypted() {
|
||||
return _slots != null;
|
||||
public static DatabaseFile fromBytes(byte[] data) throws DatabaseFileException {
|
||||
try {
|
||||
JSONObject obj = new JSONObject(new String(data, "UTF-8"));
|
||||
return DatabaseFile.fromJson(obj);
|
||||
} catch (UnsupportedEncodingException | JSONException e) {
|
||||
throw new DatabaseFileException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public JSONObject getContent() {
|
||||
return (JSONObject) _content;
|
||||
}
|
||||
|
||||
public JSONObject getContent(MasterKey key) throws DatabaseFileException {
|
||||
public JSONObject getContent(DatabaseFileCredentials creds) throws DatabaseFileException {
|
||||
try {
|
||||
byte[] bytes = Base64.decode((String) _content);
|
||||
CryptResult result = key.decrypt(bytes, _cryptParameters);
|
||||
CryptResult result = creds.decrypt(bytes, _header.getParams());
|
||||
return new JSONObject(new String(result.getData(), "UTF-8"));
|
||||
} catch (MasterKeyException | JSONException | UnsupportedEncodingException | Base64Exception e) {
|
||||
throw new DatabaseFileException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void setContent(JSONObject dbObj) {
|
||||
_content = dbObj;
|
||||
_cryptParameters = null;
|
||||
_slots = null;
|
||||
public void setContent(JSONObject obj) {
|
||||
_content = obj;
|
||||
_header = new Header(null, null);
|
||||
}
|
||||
|
||||
public void setContent(JSONObject dbObj, MasterKey key) throws DatabaseFileException {
|
||||
public void setContent(JSONObject obj, DatabaseFileCredentials creds) throws DatabaseFileException {
|
||||
try {
|
||||
String string = dbObj.toString(4);
|
||||
String string = obj.toString(4);
|
||||
byte[] dbBytes = string.getBytes("UTF-8");
|
||||
|
||||
CryptResult result = key.encrypt(dbBytes);
|
||||
CryptResult result = creds.encrypt(dbBytes);
|
||||
_content = Base64.encode(result.getData());
|
||||
_cryptParameters = result.getParams();
|
||||
_header = new Header(creds.getSlots(), result.getParams());
|
||||
} catch (MasterKeyException | UnsupportedEncodingException | JSONException e) {
|
||||
throw new DatabaseFileException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public SlotList getSlots() {
|
||||
return _slots;
|
||||
}
|
||||
public static class Header {
|
||||
private SlotList _slots;
|
||||
private CryptParameters _params;
|
||||
|
||||
public void setSlots(SlotList slots) {
|
||||
_slots = slots;
|
||||
public Header(SlotList slots, CryptParameters params) {
|
||||
_slots = slots;
|
||||
_params = params;
|
||||
}
|
||||
|
||||
public static Header fromJson(JSONObject obj) throws DatabaseFileException {
|
||||
if (obj.isNull("slots") && obj.isNull("params")) {
|
||||
return new Header(null, null);
|
||||
}
|
||||
|
||||
try {
|
||||
SlotList slots = SlotList.fromJson(obj.getJSONArray("slots"));
|
||||
CryptParameters params = CryptParameters.fromJson(obj.getJSONObject("params"));
|
||||
return new Header(slots, params);
|
||||
} catch (SlotListException | JSONException | HexException e) {
|
||||
throw new DatabaseFileException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public JSONObject toJson() {
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put("slots", _slots != null ? _slots.toJson() : JSONObject.NULL);
|
||||
obj.put("params", _params != null ? _params.toJson() : JSONObject.NULL);
|
||||
return obj;
|
||||
} catch (JSONException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public SlotList getSlots() {
|
||||
return _slots;
|
||||
}
|
||||
|
||||
public CryptParameters getParams() {
|
||||
return _params;
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return _slots == null && _params == null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
package me.impy.aegis.db;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import me.impy.aegis.crypto.CryptParameters;
|
||||
import me.impy.aegis.crypto.CryptResult;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.crypto.MasterKeyException;
|
||||
import me.impy.aegis.db.slots.SlotList;
|
||||
|
||||
public class DatabaseFileCredentials implements Serializable {
|
||||
private MasterKey _key;
|
||||
private SlotList _slots;
|
||||
|
||||
public DatabaseFileCredentials() {
|
||||
_key = MasterKey.generate();
|
||||
_slots = new SlotList();
|
||||
}
|
||||
|
||||
public DatabaseFileCredentials(MasterKey key, SlotList slots) {
|
||||
_key = key;
|
||||
_slots = slots;
|
||||
}
|
||||
|
||||
public CryptResult encrypt(byte[] bytes) throws MasterKeyException {
|
||||
return _key.encrypt(bytes);
|
||||
}
|
||||
|
||||
public CryptResult decrypt(byte[] bytes, CryptParameters params) throws MasterKeyException {
|
||||
return _key.decrypt(bytes, params);
|
||||
}
|
||||
|
||||
public MasterKey getKey() {
|
||||
return _key;
|
||||
}
|
||||
|
||||
public SlotList getSlots() {
|
||||
return _slots;
|
||||
}
|
||||
}
|
|
@ -14,17 +14,17 @@ import java.util.List;
|
|||
import java.util.UUID;
|
||||
|
||||
import me.impy.aegis.BuildConfig;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.db.slots.SlotList;
|
||||
|
||||
public class DatabaseManager {
|
||||
private static final String FILENAME = "aegis.json";
|
||||
private static final String FILENAME_EXPORT = "aegis_export.json";
|
||||
private static final String FILENAME_EXPORT_PLAIN = "aegis_export_plain.json";
|
||||
|
||||
private MasterKey _key;
|
||||
private DatabaseFile _file;
|
||||
private Database _db;
|
||||
private DatabaseFile _file;
|
||||
private DatabaseFileCredentials _creds;
|
||||
private boolean _encrypt;
|
||||
|
||||
private Context _context;
|
||||
|
||||
public DatabaseManager(Context context) {
|
||||
|
@ -39,31 +39,17 @@ public class DatabaseManager {
|
|||
public void load() throws DatabaseManagerException {
|
||||
assertState(true, false);
|
||||
|
||||
try {
|
||||
byte[] fileBytes;
|
||||
FileInputStream file = null;
|
||||
try (FileInputStream file = _context.openFileInput(FILENAME)) {
|
||||
byte[] fileBytes = new byte[(int) file.getChannel().size()];
|
||||
DataInputStream stream = new DataInputStream(file);
|
||||
stream.readFully(fileBytes);
|
||||
stream.close();
|
||||
|
||||
try {
|
||||
file = _context.openFileInput(FILENAME);
|
||||
fileBytes = new byte[(int) file.getChannel().size()];
|
||||
DataInputStream stream = new DataInputStream(file);
|
||||
stream.readFully(fileBytes);
|
||||
stream.close();
|
||||
} finally {
|
||||
// always close the file stream
|
||||
// there is no need to close the DataInputStream
|
||||
if (file != null) {
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
_file = new DatabaseFile();
|
||||
_file.deserialize(fileBytes);
|
||||
|
||||
if (!_file.isEncrypted()) {
|
||||
_file = DatabaseFile.fromBytes(fileBytes);
|
||||
_encrypt = _file.isEncrypted();
|
||||
if (!isEncryptionEnabled()) {
|
||||
JSONObject obj = _file.getContent();
|
||||
_db = new Database();
|
||||
_db.deserialize(obj);
|
||||
_db = Database.fromJson(obj);
|
||||
}
|
||||
} catch (IOException | DatabaseFileException | DatabaseException e) {
|
||||
throw new DatabaseManagerException(e);
|
||||
|
@ -73,37 +59,26 @@ public class DatabaseManager {
|
|||
public void lock() {
|
||||
assertState(false, true);
|
||||
// TODO: properly clear everything
|
||||
_key = null;
|
||||
_creds = null;
|
||||
_db = null;
|
||||
}
|
||||
|
||||
public void unlock(MasterKey key) throws DatabaseManagerException {
|
||||
public void unlock(DatabaseFileCredentials creds) throws DatabaseManagerException {
|
||||
assertState(true, true);
|
||||
|
||||
try {
|
||||
JSONObject obj = _file.getContent(key);
|
||||
_db = new Database();
|
||||
_db.deserialize(obj);
|
||||
_key = key;
|
||||
JSONObject obj = _file.getContent(creds);
|
||||
_db = Database.fromJson(obj);
|
||||
_creds = creds;
|
||||
} catch (DatabaseFileException | DatabaseException e) {
|
||||
throw new DatabaseManagerException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void save(Context context, DatabaseFile file) throws DatabaseManagerException {
|
||||
try {
|
||||
byte[] bytes = file.serialize();
|
||||
|
||||
FileOutputStream stream = null;
|
||||
try {
|
||||
stream = context.openFileOutput(FILENAME, Context.MODE_PRIVATE);
|
||||
stream.write(bytes);
|
||||
} finally {
|
||||
// always close the file stream
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
byte[] bytes = file.toBytes();
|
||||
try (FileOutputStream stream = context.openFileOutput(FILENAME, Context.MODE_PRIVATE)) {
|
||||
stream.write(bytes);
|
||||
} catch (IOException e) {
|
||||
throw new DatabaseManagerException(e);
|
||||
}
|
||||
|
@ -113,9 +88,9 @@ public class DatabaseManager {
|
|||
assertState(false, true);
|
||||
|
||||
try {
|
||||
JSONObject obj = _db.serialize();
|
||||
if (_file.isEncrypted()) {
|
||||
_file.setContent(obj, _key);
|
||||
JSONObject obj = _db.toJson();
|
||||
if (isEncryptionEnabled()) {
|
||||
_file.setContent(obj, _creds);
|
||||
} else {
|
||||
_file.setContent(obj);
|
||||
}
|
||||
|
@ -130,31 +105,22 @@ public class DatabaseManager {
|
|||
|
||||
try {
|
||||
DatabaseFile dbFile = new DatabaseFile();
|
||||
dbFile.setSlots(_file.getSlots());
|
||||
if (encrypt && getFile().isEncrypted()) {
|
||||
dbFile.setContent(_db.serialize(), _key);
|
||||
if (encrypt && isEncryptionEnabled()) {
|
||||
dbFile.setContent(_db.toJson(), _creds);
|
||||
} else {
|
||||
dbFile.setContent(_db.serialize());
|
||||
dbFile.setContent(_db.toJson());
|
||||
}
|
||||
|
||||
File file;
|
||||
FileOutputStream stream = null;
|
||||
try {
|
||||
String dirName = !BuildConfig.DEBUG ? "Aegis" : "AegisDebug";
|
||||
File dir = new File(Environment.getExternalStorageDirectory(), dirName);
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException("error creating external storage directory");
|
||||
}
|
||||
String dirName = !BuildConfig.DEBUG ? "Aegis" : "AegisDebug";
|
||||
File dir = new File(Environment.getExternalStorageDirectory(), dirName);
|
||||
if (!dir.exists() && !dir.mkdirs()) {
|
||||
throw new IOException("error creating external storage directory");
|
||||
}
|
||||
|
||||
byte[] bytes = dbFile.serialize();
|
||||
file = new File(dir.getAbsolutePath(), encrypt ? FILENAME_EXPORT : FILENAME_EXPORT_PLAIN);
|
||||
stream = new FileOutputStream(file);
|
||||
byte[] bytes = dbFile.toBytes();
|
||||
File file = new File(dir.getAbsolutePath(), encrypt ? FILENAME_EXPORT : FILENAME_EXPORT_PLAIN);
|
||||
try (FileOutputStream stream = new FileOutputStream(file)) {
|
||||
stream.write(bytes);
|
||||
} finally {
|
||||
// always close the file stream
|
||||
if (stream != null) {
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
return file.getAbsolutePath();
|
||||
|
@ -193,25 +159,38 @@ public class DatabaseManager {
|
|||
return _db.getEntryByUUID(uuid);
|
||||
}
|
||||
|
||||
public MasterKey getMasterKey() {
|
||||
public DatabaseFileCredentials getCredentials() {
|
||||
assertState(false, true);
|
||||
return _key;
|
||||
return _creds;
|
||||
}
|
||||
|
||||
public DatabaseFile getFile() {
|
||||
return _file;
|
||||
public void setCredentials(DatabaseFileCredentials creds) {
|
||||
assertState(false, true);
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
public void enableEncryption(MasterKey key, SlotList slots) {
|
||||
assertState(false, true);
|
||||
_key = key;
|
||||
_file.setSlots(slots);
|
||||
public DatabaseFile.Header getFileHeader() {
|
||||
assertLoaded(true);
|
||||
return _file.getHeader();
|
||||
}
|
||||
|
||||
public void disableEncryption() {
|
||||
public boolean isEncryptionEnabled() {
|
||||
assertLoaded(true);
|
||||
return _encrypt;
|
||||
}
|
||||
|
||||
public void enableEncryption(DatabaseFileCredentials creds) throws DatabaseManagerException {
|
||||
assertState(false, true);
|
||||
_key = null;
|
||||
_file.setSlots(null);
|
||||
_creds = creds;
|
||||
_encrypt = true;
|
||||
save();
|
||||
}
|
||||
|
||||
public void disableEncryption() throws DatabaseManagerException {
|
||||
assertState(false, true);
|
||||
_creds = null;
|
||||
_encrypt = false;
|
||||
save();
|
||||
}
|
||||
|
||||
public boolean isLoaded() {
|
||||
|
@ -223,16 +202,20 @@ public class DatabaseManager {
|
|||
}
|
||||
|
||||
private void assertState(boolean locked, boolean loaded) {
|
||||
if (isLoaded() && !loaded) {
|
||||
throw new AssertionError("database file has not been loaded yet");
|
||||
} else if (!isLoaded() && loaded) {
|
||||
throw new AssertionError("database file has is already been loaded");
|
||||
}
|
||||
assertLoaded(loaded);
|
||||
|
||||
if (isLocked() && !locked) {
|
||||
throw new AssertionError("database file has not been unlocked yet");
|
||||
} else if (!isLocked() && locked) {
|
||||
throw new AssertionError("database file has is already been unlocked");
|
||||
throw new AssertionError("database file has already been unlocked");
|
||||
}
|
||||
}
|
||||
|
||||
private void assertLoaded(boolean loaded) {
|
||||
if (isLoaded() && !loaded) {
|
||||
throw new AssertionError("database file has already been loaded");
|
||||
} else if (!isLoaded() && loaded) {
|
||||
throw new AssertionError("database file has not been loaded yet");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,9 +20,9 @@ public class PasswordSlot extends RawSlot {
|
|||
}
|
||||
|
||||
@Override
|
||||
public JSONObject serialize() {
|
||||
public JSONObject toJson() {
|
||||
try {
|
||||
JSONObject obj = super.serialize();
|
||||
JSONObject obj = super.toJson();
|
||||
obj.put("n", _n);
|
||||
obj.put("r", _r);
|
||||
obj.put("p", _p);
|
||||
|
|
|
@ -10,7 +10,6 @@ import java.security.InvalidKeyException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.crypto.AEADBadTagException;
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
|
@ -85,7 +84,7 @@ public abstract class Slot implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public JSONObject serialize() {
|
||||
public JSONObject toJson() {
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
JSONObject paramObj = _encryptedMasterKeyParams.toJson();
|
||||
|
@ -114,7 +113,7 @@ public abstract class Slot implements Serializable {
|
|||
|
||||
JSONObject paramObj = obj.getJSONObject("key_params");
|
||||
_encryptedMasterKey = Hex.decode(obj.getString("key"));
|
||||
_encryptedMasterKeyParams = CryptParameters.parseJson(paramObj);
|
||||
_encryptedMasterKeyParams = CryptParameters.fromJson(paramObj);
|
||||
} catch (JSONException | HexException e) {
|
||||
throw new SlotException(e);
|
||||
}
|
||||
|
|
|
@ -12,16 +12,16 @@ import java.util.List;
|
|||
public class SlotList implements Iterable<Slot>, Serializable {
|
||||
private List<Slot> _slots = new ArrayList<>();
|
||||
|
||||
public static JSONArray serialize(SlotList slots) {
|
||||
public JSONArray toJson() {
|
||||
JSONArray array = new JSONArray();
|
||||
for (Slot slot : slots) {
|
||||
array.put(slot.serialize());
|
||||
for (Slot slot : this) {
|
||||
array.put(slot.toJson());
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
public static SlotList deserialize(JSONArray array) throws SlotListException {
|
||||
public static SlotList fromJson(JSONArray array) throws SlotListException {
|
||||
SlotList slots = new SlotList();
|
||||
|
||||
try {
|
||||
|
|
|
@ -4,16 +4,16 @@ import org.json.JSONObject;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.db.Database;
|
||||
import me.impy.aegis.db.DatabaseEntry;
|
||||
import me.impy.aegis.db.DatabaseException;
|
||||
import me.impy.aegis.db.DatabaseFile;
|
||||
import me.impy.aegis.db.DatabaseFileCredentials;
|
||||
import me.impy.aegis.db.DatabaseFileException;
|
||||
import me.impy.aegis.util.ByteInputStream;
|
||||
|
||||
public class AegisImporter extends DatabaseImporter {
|
||||
private MasterKey _key;
|
||||
private DatabaseFileCredentials _creds;
|
||||
private DatabaseFile _file;
|
||||
|
||||
public AegisImporter(ByteInputStream stream) {
|
||||
|
@ -24,8 +24,7 @@ public class AegisImporter extends DatabaseImporter {
|
|||
public void parse() throws DatabaseImporterException {
|
||||
try {
|
||||
byte[] bytes = _stream.getBytes();
|
||||
_file = new DatabaseFile();
|
||||
_file.deserialize(bytes);
|
||||
_file = DatabaseFile.fromBytes(bytes);
|
||||
} catch (DatabaseFileException e) {
|
||||
throw new DatabaseImporterException(e);
|
||||
}
|
||||
|
@ -35,14 +34,13 @@ public class AegisImporter extends DatabaseImporter {
|
|||
public List<DatabaseEntry> convert() throws DatabaseImporterException {
|
||||
try {
|
||||
JSONObject obj;
|
||||
if (_file.isEncrypted() && _key != null) {
|
||||
obj = _file.getContent(_key);
|
||||
if (_file.isEncrypted() && _creds != null) {
|
||||
obj = _file.getContent(_creds);
|
||||
} else {
|
||||
obj = _file.getContent();
|
||||
}
|
||||
|
||||
Database db = new Database();
|
||||
db.deserialize(obj);
|
||||
Database db = Database.fromJson(obj);
|
||||
return db.getEntries();
|
||||
} catch (DatabaseException | DatabaseFileException e) {
|
||||
throw new DatabaseImporterException(e);
|
||||
|
@ -54,8 +52,8 @@ public class AegisImporter extends DatabaseImporter {
|
|||
return _file.isEncrypted();
|
||||
}
|
||||
|
||||
public void setKey(MasterKey key) {
|
||||
_key = key;
|
||||
public void setCredentials(DatabaseFileCredentials creds) {
|
||||
_creds = creds;
|
||||
}
|
||||
|
||||
public DatabaseFile getFile() {
|
||||
|
|
|
@ -42,14 +42,6 @@ public abstract class DatabaseImporter {
|
|||
}
|
||||
}
|
||||
|
||||
public static List<DatabaseImporter> create(ByteInputStream stream) {
|
||||
List<DatabaseImporter> list = new ArrayList<>();
|
||||
for (Class<? extends DatabaseImporter> type : _importers.values()) {
|
||||
list.add(create(stream, type));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public static Map<String, Class<? extends DatabaseImporter>> getImporters() {
|
||||
return _importers;
|
||||
}
|
||||
|
|
|
@ -88,7 +88,7 @@ public abstract class OtpInfo implements Serializable {
|
|||
_digits = digits;
|
||||
}
|
||||
|
||||
public static OtpInfo parseJson(String type, JSONObject obj) throws OtpInfoException {
|
||||
public static OtpInfo fromJson(String type, JSONObject obj) throws OtpInfoException {
|
||||
OtpInfo info;
|
||||
|
||||
try {
|
||||
|
|
|
@ -25,6 +25,7 @@ import me.impy.aegis.R;
|
|||
import me.impy.aegis.crypto.KeyStoreHandle;
|
||||
import me.impy.aegis.crypto.KeyStoreHandleException;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.db.DatabaseFileCredentials;
|
||||
import me.impy.aegis.db.slots.FingerprintSlot;
|
||||
import me.impy.aegis.db.slots.PasswordSlot;
|
||||
import me.impy.aegis.db.slots.Slot;
|
||||
|
@ -128,7 +129,7 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C
|
|||
private void setKey(MasterKey key) {
|
||||
// send the master key back to the main activity
|
||||
Intent result = new Intent();
|
||||
result.putExtra("key", key);
|
||||
result.putExtra("creds", new DatabaseFileCredentials(key, _slots));
|
||||
setResult(RESULT_OK, result);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@ import javax.crypto.SecretKey;
|
|||
|
||||
import me.impy.aegis.Preferences;
|
||||
import me.impy.aegis.R;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.db.DatabaseFileCredentials;
|
||||
import me.impy.aegis.db.DatabaseFileException;
|
||||
import me.impy.aegis.db.DatabaseManagerException;
|
||||
import me.impy.aegis.db.slots.FingerprintSlot;
|
||||
|
@ -128,9 +128,9 @@ public class IntroActivity extends AppIntro2 implements DerivationTask.Callback
|
|||
}
|
||||
|
||||
// generate the master key
|
||||
MasterKey masterKey = null;
|
||||
DatabaseFileCredentials creds = null;
|
||||
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
||||
masterKey = MasterKey.generate();
|
||||
creds = new DatabaseFileCredentials();
|
||||
}
|
||||
|
||||
SlotList slots = null;
|
||||
|
@ -141,10 +141,8 @@ public class IntroActivity extends AppIntro2 implements DerivationTask.Callback
|
|||
throw new RuntimeException();
|
||||
}
|
||||
try {
|
||||
_passwordSlot.setKey(masterKey, _passwordCipher);
|
||||
slots = new SlotList();
|
||||
slots.add(_passwordSlot);
|
||||
_databaseFile.setSlots(slots);
|
||||
_passwordSlot.setKey(creds.getKey(), _passwordCipher);
|
||||
creds.getSlots().add(_passwordSlot);
|
||||
} catch (SlotException e) {
|
||||
setException(e);
|
||||
}
|
||||
|
@ -156,8 +154,8 @@ public class IntroActivity extends AppIntro2 implements DerivationTask.Callback
|
|||
// and add it to the list of slots
|
||||
FingerprintSlot slot = _authenticatedSlide.getFingerSlot();
|
||||
Cipher cipher = _authenticatedSlide.getFingerCipher();
|
||||
slot.setKey(masterKey, cipher);
|
||||
slots.add(slot);
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
creds.getSlots().add(slot);
|
||||
} catch (SlotException e) {
|
||||
setException(e);
|
||||
return;
|
||||
|
@ -166,11 +164,11 @@ public class IntroActivity extends AppIntro2 implements DerivationTask.Callback
|
|||
|
||||
// finally, save the database
|
||||
try {
|
||||
JSONObject obj = _database.serialize();
|
||||
JSONObject obj = _database.toJson();
|
||||
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
||||
_databaseFile.setContent(obj);
|
||||
} else {
|
||||
_databaseFile.setContent(obj, masterKey);
|
||||
_databaseFile.setContent(obj, creds);
|
||||
}
|
||||
DatabaseManager.save(getApplicationContext(), _databaseFile);
|
||||
} catch (DatabaseManagerException | DatabaseFileException e) {
|
||||
|
@ -180,7 +178,7 @@ public class IntroActivity extends AppIntro2 implements DerivationTask.Callback
|
|||
|
||||
// send the master key back to the main activity
|
||||
Intent result = new Intent();
|
||||
result.putExtra("key", masterKey);
|
||||
result.putExtra("creds", creds);
|
||||
setResult(RESULT_OK, result);
|
||||
|
||||
// skip the intro from now on
|
||||
|
|
|
@ -20,7 +20,7 @@ import java.lang.reflect.UndeclaredThrowableException;
|
|||
|
||||
import me.impy.aegis.AegisApplication;
|
||||
import me.impy.aegis.R;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.db.DatabaseFileCredentials;
|
||||
import me.impy.aegis.db.DatabaseManagerException;
|
||||
import me.impy.aegis.db.DatabaseEntry;
|
||||
import me.impy.aegis.db.DatabaseManager;
|
||||
|
@ -218,13 +218,13 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
|
||||
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
||||
unlockDatabase(key);
|
||||
DatabaseFileCredentials creds = (DatabaseFileCredentials) data.getSerializableExtra("creds");
|
||||
unlockDatabase(creds);
|
||||
}
|
||||
|
||||
private void onDecryptResult(int resultCode, Intent intent) {
|
||||
MasterKey key = (MasterKey) intent.getSerializableExtra("key");
|
||||
unlockDatabase(key);
|
||||
DatabaseFileCredentials creds = (DatabaseFileCredentials) intent.getSerializableExtra("creds");
|
||||
unlockDatabase(creds);
|
||||
|
||||
doShortcutActions();
|
||||
}
|
||||
|
@ -361,7 +361,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
}
|
||||
}
|
||||
|
||||
private void unlockDatabase(MasterKey key) {
|
||||
private void unlockDatabase(DatabaseFileCredentials creds) {
|
||||
if (_loaded) {
|
||||
return;
|
||||
}
|
||||
|
@ -371,15 +371,14 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
_db.load();
|
||||
}
|
||||
if (_db.isLocked()) {
|
||||
if (key == null) {
|
||||
if (creds == null) {
|
||||
startAuthActivity();
|
||||
return;
|
||||
} else {
|
||||
_db.unlock(key);
|
||||
_db.unlock(creds);
|
||||
}
|
||||
}
|
||||
} catch (DatabaseManagerException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(this, "An error occurred while trying to load/decrypt the database", Toast.LENGTH_LONG).show();
|
||||
startAuthActivity();
|
||||
return;
|
||||
|
@ -396,7 +395,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
|
||||
private void startAuthActivity() {
|
||||
Intent intent = new Intent(this, AuthActivity.class);
|
||||
intent.putExtra("slots", _db.getFile().getSlots());
|
||||
intent.putExtra("slots", _db.getFileHeader().getSlots());
|
||||
startActivityForResult(intent, CODE_DECRYPT);
|
||||
}
|
||||
|
||||
|
@ -404,7 +403,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
try {
|
||||
_db.save();
|
||||
} catch (DatabaseManagerException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(this, "An error occurred while trying to save the database", Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
|
@ -413,7 +411,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene
|
|||
// hide the lock icon if the database is not unlocked
|
||||
if (_menu != null && !_db.isLocked()) {
|
||||
MenuItem item = _menu.findItem(R.id.action_lock);
|
||||
item.setVisible(_db.getFile().isEncrypted());
|
||||
item.setVisible(_db.isEncryptionEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -26,12 +26,12 @@ 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.DatabaseFileCredentials;
|
||||
import me.impy.aegis.db.DatabaseFileException;
|
||||
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.SlotList;
|
||||
import me.impy.aegis.db.slots.SlotException;
|
||||
import me.impy.aegis.helpers.PermissionHelper;
|
||||
import me.impy.aegis.importers.AegisImporter;
|
||||
|
@ -138,7 +138,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
_encryptionPreference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
if (!_db.getFile().isEncrypted()) {
|
||||
if (!_db.isEncryptionEnabled()) {
|
||||
PasswordDialogFragment dialog = new PasswordDialogFragment();
|
||||
// TODO: find a less ugly way to obtain the fragment manager
|
||||
dialog.show(getActivity().getSupportFragmentManager(), null);
|
||||
|
@ -148,8 +148,11 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
.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();
|
||||
try {
|
||||
_db.disableEncryption();
|
||||
} catch (DatabaseManagerException e) {
|
||||
Toast.makeText(getActivity(), "An error occurred while enabling encryption", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
updateEncryptionPreference();
|
||||
}
|
||||
})
|
||||
|
@ -163,10 +166,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
_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());
|
||||
intent.putExtra("creds", _db.getCredentials());
|
||||
startActivityForResult(intent, CODE_SLOTS);
|
||||
return true;
|
||||
}
|
||||
|
@ -249,13 +250,12 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
return;
|
||||
}
|
||||
|
||||
MasterKey key = (MasterKey) data.getSerializableExtra("key");
|
||||
((AegisImporter)_importer).setKey(key);
|
||||
DatabaseFileCredentials creds = (DatabaseFileCredentials) data.getSerializableExtra("creds");
|
||||
((AegisImporter)_importer).setCredentials(creds);
|
||||
|
||||
try {
|
||||
importDatabase(_importer);
|
||||
} catch (DatabaseImporterException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), "An error occurred while trying to parse the file", Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
|
@ -269,25 +269,16 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
}
|
||||
|
||||
ByteInputStream stream;
|
||||
InputStream fileStream = null;
|
||||
|
||||
try {
|
||||
fileStream = getActivity().getContentResolver().openInputStream(uri);
|
||||
try (InputStream fileStream = getActivity().getContentResolver().openInputStream(uri)) {
|
||||
stream = ByteInputStream.create(fileStream);
|
||||
} catch (FileNotFoundException e) {
|
||||
Toast.makeText(getActivity(), "Error: File not found", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), "An error occurred while trying to read the file", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
} finally {
|
||||
if (fileStream != null) {
|
||||
try {
|
||||
fileStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
|
@ -299,7 +290,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
_importer = importer;
|
||||
|
||||
Intent intent = new Intent(getActivity(), AuthActivity.class);
|
||||
intent.putExtra("slots", ((AegisImporter)_importer).getFile().getSlots());
|
||||
intent.putExtra("slots", ((AegisImporter)_importer).getFile().getHeader().getSlots());
|
||||
startActivityForResult(intent, CODE_IMPORT_DECRYPT);
|
||||
return;
|
||||
}
|
||||
|
@ -344,7 +335,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
try {
|
||||
filename = _db.export(checked[0]);
|
||||
} catch (DatabaseManagerException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), "An error occurred while trying to export the database", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
@ -355,7 +345,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
Toast.makeText(getActivity(), "The database has been exported to: " + filename, Toast.LENGTH_SHORT).show();
|
||||
})
|
||||
.setNegativeButton(android.R.string.cancel, null);
|
||||
if (_db.getFile().isEncrypted()) {
|
||||
if (_db.isEncryptionEnabled()) {
|
||||
final String[] items = {"Keep the database encrypted"};
|
||||
final boolean[] checkedItems = {true};
|
||||
builder.setMultiChoiceItems(items, checkedItems, new DialogInterface.OnMultiChoiceClickListener() {
|
||||
|
@ -375,8 +365,8 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
return;
|
||||
}
|
||||
|
||||
SlotList slots = (SlotList) data.getSerializableExtra("slots");
|
||||
_db.getFile().setSlots(slots);
|
||||
DatabaseFileCredentials creds = (DatabaseFileCredentials) data.getSerializableExtra("creds");
|
||||
_db.setCredentials(creds);
|
||||
saveDatabase();
|
||||
}
|
||||
|
||||
|
@ -384,7 +374,6 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
try {
|
||||
_db.save();
|
||||
} catch (DatabaseManagerException e) {
|
||||
e.printStackTrace();
|
||||
Toast.makeText(getActivity(), "An error occurred while trying to save the database", Toast.LENGTH_LONG).show();
|
||||
return false;
|
||||
}
|
||||
|
@ -394,19 +383,17 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
|
||||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
MasterKey masterKey = MasterKey.generate();
|
||||
DatabaseFileCredentials creds = new DatabaseFileCredentials();
|
||||
|
||||
SlotList slots = new SlotList();
|
||||
try {
|
||||
slot.setKey(masterKey, cipher);
|
||||
} catch (SlotException e) {
|
||||
slot.setKey(creds.getKey(), cipher);
|
||||
creds.getSlots().add(slot);
|
||||
_db.enableEncryption(creds);
|
||||
} catch (DatabaseManagerException | SlotException e) {
|
||||
onException(e);
|
||||
return;
|
||||
}
|
||||
slots.add(slot);
|
||||
_db.enableEncryption(masterKey, slots);
|
||||
|
||||
saveDatabase();
|
||||
updateEncryptionPreference();
|
||||
}
|
||||
|
||||
|
@ -417,7 +404,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
|
|||
}
|
||||
|
||||
private void updateEncryptionPreference() {
|
||||
boolean encrypted = _db.getFile().isEncrypted();
|
||||
boolean encrypted = _db.isEncryptionEnabled();
|
||||
_encryptionPreference.setChecked(encrypted, true);
|
||||
_slotsPreference.setEnabled(encrypted);
|
||||
}
|
||||
|
|
|
@ -110,10 +110,7 @@ public class ScannerActivity extends AegisActivity implements ZXingScannerView.R
|
|||
// parse google auth uri
|
||||
Uri uri = Uri.parse(rawResult.getText());
|
||||
GoogleAuthInfo info = GoogleAuthInfo.parseUri(uri);
|
||||
|
||||
DatabaseEntry entry = new DatabaseEntry(info.getOtpInfo());
|
||||
entry.setIssuer(info.getIssuer());
|
||||
entry.setName(info.getAccountName());
|
||||
DatabaseEntry entry = new DatabaseEntry(info);
|
||||
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra("entry", entry);
|
||||
|
|
|
@ -16,7 +16,7 @@ import javax.crypto.Cipher;
|
|||
import me.impy.aegis.R;
|
||||
import me.impy.aegis.crypto.KeyStoreHandle;
|
||||
import me.impy.aegis.crypto.KeyStoreHandleException;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.db.DatabaseFileCredentials;
|
||||
import me.impy.aegis.db.slots.FingerprintSlot;
|
||||
import me.impy.aegis.db.slots.PasswordSlot;
|
||||
import me.impy.aegis.db.slots.Slot;
|
||||
|
@ -29,8 +29,7 @@ import me.impy.aegis.ui.views.SlotAdapter;
|
|||
import me.impy.aegis.ui.dialogs.SlotDialogFragment;
|
||||
|
||||
public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Listener, SlotDialogFragment.Listener {
|
||||
private MasterKey _masterKey;
|
||||
private SlotList _slots;
|
||||
private DatabaseFileCredentials _creds;
|
||||
private SlotAdapter _adapter;
|
||||
|
||||
private boolean _edited;
|
||||
|
@ -59,9 +58,8 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
|
|||
slotsView.setNestedScrollingEnabled(false);
|
||||
|
||||
// load the slots and masterKey
|
||||
_masterKey = (MasterKey) getIntent().getSerializableExtra("masterKey");
|
||||
_slots = (SlotList) getIntent().getSerializableExtra("slots");
|
||||
for (Slot slot : _slots) {
|
||||
_creds = (DatabaseFileCredentials) getIntent().getSerializableExtra("creds");
|
||||
for (Slot slot : _creds.getSlots()) {
|
||||
_adapter.addSlot(slot);
|
||||
}
|
||||
|
||||
|
@ -75,7 +73,7 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
|
|||
if (FingerprintHelper.getManager(this) != null) {
|
||||
try {
|
||||
KeyStoreHandle keyStore = new KeyStoreHandle();
|
||||
for (FingerprintSlot slot : _slots.findAll(FingerprintSlot.class)) {
|
||||
for (FingerprintSlot slot : _creds.getSlots().findAll(FingerprintSlot.class)) {
|
||||
if (keyStore.containsKey(slot.getUUID().toString())) {
|
||||
visibility = View.GONE;
|
||||
break;
|
||||
|
@ -92,7 +90,7 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
|
|||
|
||||
private void onSave() {
|
||||
Intent intent = new Intent();
|
||||
intent.putExtra("slots", _slots);
|
||||
intent.putExtra("creds", _creds);
|
||||
setResult(RESULT_OK, intent);
|
||||
finish();
|
||||
}
|
||||
|
@ -150,7 +148,8 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
|
|||
|
||||
@Override
|
||||
public void onRemoveSlot(Slot slot) {
|
||||
if (slot instanceof PasswordSlot && _slots.findAll(PasswordSlot.class).size() <= 1) {
|
||||
SlotList slots = _creds.getSlots();
|
||||
if (slot instanceof PasswordSlot && slots.findAll(PasswordSlot.class).size() <= 1) {
|
||||
Toast.makeText(this, "You must have at least one password slot", Toast.LENGTH_SHORT).show();
|
||||
return;
|
||||
}
|
||||
|
@ -159,7 +158,7 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
|
|||
.setTitle("Remove slot")
|
||||
.setMessage("Are you sure you want to remove this slot?")
|
||||
.setPositiveButton(android.R.string.yes, (dialog, whichButton) -> {
|
||||
_slots.remove(slot);
|
||||
slots.remove(slot);
|
||||
_adapter.removeSlot(slot);
|
||||
_edited = true;
|
||||
updateFingerprintButton();
|
||||
|
@ -171,13 +170,13 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
|
|||
@Override
|
||||
public void onSlotResult(Slot slot, Cipher cipher) {
|
||||
try {
|
||||
slot.setKey(_masterKey, cipher);
|
||||
slot.setKey(_creds.getKey(), cipher);
|
||||
} catch (SlotException e) {
|
||||
onException(e);
|
||||
return;
|
||||
}
|
||||
|
||||
_slots.add(slot);
|
||||
_creds.getSlots().add(slot);
|
||||
_adapter.addSlot(slot);
|
||||
_edited = true;
|
||||
updateFingerprintButton();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue