Move to an entirely JSON encoded file for the database

This commit is contained in:
Alexander Bakker 2018-02-13 22:06:24 +01:00
parent 7eaffc933e
commit 587835ad38
14 changed files with 187 additions and 297 deletions

View file

@ -13,11 +13,7 @@ public class Database {
private List<DatabaseEntry> _entries = new ArrayList<>(); private List<DatabaseEntry> _entries = new ArrayList<>();
private long _counter = 0; private long _counter = 0;
public byte[] serialize() throws Exception { public JSONObject serialize() throws Exception {
return serialize(false);
}
public byte[] serialize(boolean pretty) throws Exception {
JSONArray array = new JSONArray(); JSONArray array = new JSONArray();
for (DatabaseEntry e : _entries) { for (DatabaseEntry e : _entries) {
array.put(e.serialize()); array.put(e.serialize());
@ -26,18 +22,14 @@ public class Database {
JSONObject obj = new JSONObject(); JSONObject obj = new JSONObject();
obj.put("version", VERSION); obj.put("version", VERSION);
obj.put("entries", array); obj.put("entries", array);
return obj;
String string = pretty ? obj.toString(4) : obj.toString();
return string.getBytes("UTF-8");
} }
public void deserialize(byte[] data) throws Exception { public void deserialize(JSONObject obj) throws Exception {
deserialize(data, true); deserialize(obj, true);
} }
public void deserialize(byte[] data, boolean incCount) throws Exception { public void deserialize(JSONObject obj, boolean incCount) throws Exception {
JSONObject obj = new JSONObject(new String(data, "UTF-8"));
// TODO: support different VERSION deserialization providers // TODO: support different VERSION deserialization providers
int ver = obj.getInt("version"); int ver = obj.getInt("version");
if (ver != VERSION) { if (ver != VERSION) {

View file

@ -1,163 +1,116 @@
package me.impy.aegis.db; package me.impy.aegis.db;
import java.io.ByteArrayOutputStream; import android.util.Base64;
import java.io.DataOutputStream;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException; import java.io.UnsupportedEncodingException;
import java.util.Arrays; import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import me.impy.aegis.crypto.CryptParameters; import me.impy.aegis.crypto.CryptParameters;
import me.impy.aegis.crypto.CryptResult;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.db.slots.SlotCollection; import me.impy.aegis.db.slots.SlotCollection;
import me.impy.aegis.crypto.CryptoUtils; import me.impy.aegis.encoding.Hex;
import me.impy.aegis.util.LittleByteBuffer;
public class DatabaseFile { public class DatabaseFile {
private static final byte SECTION_ENCRYPTION_PARAMETERS = 0x00; public static final byte VERSION = 1;
private static final byte SECTION_SLOTS = 0x01;
private static final byte SECTION_END = (byte) 0xFF;
private static final byte VERSION = 1;
private final byte[] HEADER; private Object _content;
private byte[] _content;
private CryptParameters _cryptParameters; private CryptParameters _cryptParameters;
private SlotCollection _slots; private SlotCollection _slots;
public DatabaseFile() { public DatabaseFile() {
try {
HEADER = "AEGIS".getBytes("US_ASCII");
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}
_slots = new SlotCollection(); _slots = new SlotCollection();
} }
public byte[] serialize() throws IOException { public byte[] serialize() throws JSONException, UnsupportedEncodingException {
byte[] content = getContent(); JSONObject cryptObj = null;
CryptParameters cryptParams = getCryptParameters(); if (_cryptParameters != null) {
cryptObj = new JSONObject();
// this is dumb, java doesn't provide an endianness-aware data stream cryptObj.put("nonce", Hex.toString(_cryptParameters.Nonce));
ByteArrayOutputStream byteStream = new ByteArrayOutputStream(); cryptObj.put("tag", Hex.toString(_cryptParameters.Tag));
DataOutputStream stream = new DataOutputStream(byteStream);
stream.write(HEADER);
stream.write(VERSION);
if (cryptParams != null) {
LittleByteBuffer paramBuffer = LittleByteBuffer.allocate(CryptoUtils.CRYPTO_NONCE_SIZE + CryptoUtils.CRYPTO_TAG_SIZE);
paramBuffer.put(cryptParams.Nonce);
paramBuffer.put(cryptParams.Tag);
writeSection(stream, SECTION_ENCRYPTION_PARAMETERS, paramBuffer.array());
} }
if (!_slots.isEmpty()) { JSONObject headerObj = new JSONObject();
byte[] bytes = SlotCollection.serialize(_slots); headerObj.put("slots", _slots.isEmpty() ? JSONObject.NULL : SlotCollection.serialize(_slots));
writeSection(stream, SECTION_SLOTS, bytes); headerObj.put("params", cryptObj != null ? cryptObj : JSONObject.NULL);
}
writeSection(stream, SECTION_END, null); JSONObject obj = new JSONObject();
stream.write(content); obj.put("version", VERSION);
return byteStream.toByteArray(); obj.put("header", headerObj);
obj.put("db", _content);
String string = obj.toString(4);
return string.getBytes("UTF-8");
} }
public void deserialize(byte[] data) throws Exception { public void deserialize(byte[] data) throws Exception {
LittleByteBuffer buffer = LittleByteBuffer.wrap(data); JSONObject obj = new JSONObject(new String(data, "UTF-8"));
JSONObject headerObj = obj.getJSONObject("header");
byte[] header = new byte[HEADER.length]; if (obj.getInt("version") > VERSION) {
buffer.get(header); throw new Exception("unsupported version");
if (!Arrays.equals(header, HEADER)) {
throw new Exception("Bad header");
} }
// TODO: support different version deserialization providers JSONObject slotObj = headerObj.optJSONObject("slots");
byte version = buffer.get(); if (slotObj != null) {
if (version != VERSION) { _slots = SlotCollection.deserialize(slotObj);
throw new Exception("Unsupported version");
} }
CryptParameters cryptParams = null; JSONObject cryptObj = headerObj.optJSONObject("params");
SlotCollection slots = new SlotCollection(); if (cryptObj != null) {
_cryptParameters = new CryptParameters() {{
for (section s = readSection(buffer); s.ID != SECTION_END; s = readSection(buffer)) { Nonce = Hex.toBytes(cryptObj.getString("nonce"));
LittleByteBuffer sBuff = LittleByteBuffer.wrap(s.Data); Tag = Hex.toBytes(cryptObj.getString("tag"));
switch (s.ID) {
case SECTION_ENCRYPTION_PARAMETERS:
assertLength(s.Data, CryptoUtils.CRYPTO_NONCE_SIZE + CryptoUtils.CRYPTO_TAG_SIZE);
byte[] nonce = new byte[CryptoUtils.CRYPTO_NONCE_SIZE];
byte[] tag = new byte[CryptoUtils.CRYPTO_TAG_SIZE];
sBuff.get(nonce);
sBuff.get(tag);
cryptParams = new CryptParameters() {{
Nonce = nonce;
Tag = tag;
}}; }};
break;
case SECTION_SLOTS:
slots = SlotCollection.deserialize(s.Data);
break;
}
} }
setCryptParameters(cryptParams); if (cryptObj == null || slotObj == null) {
setSlots(slots); _content = obj.getJSONObject("db");
} else {
byte[] content = new byte[buffer.remaining()]; _content = obj.getString("db");
buffer.get(content); }
setContent(content);
} }
public boolean isEncrypted() { public boolean isEncrypted() {
return !_slots.isEmpty() && _cryptParameters != null; return !_slots.isEmpty() && _cryptParameters != null;
} }
private static void writeSection(DataOutputStream stream, byte id, byte[] data) throws IOException { public JSONObject getContent() {
stream.write(id); return (JSONObject) _content;
LittleByteBuffer buffer = LittleByteBuffer.allocate(/* sizeof uint32_t */ 4);
if (data == null) {
buffer.putInt(0);
} else {
buffer.putInt(data.length);
}
stream.write(buffer.array());
if (data != null) {
stream.write(data);
}
} }
private static section readSection(LittleByteBuffer buffer) { public JSONObject getContent(MasterKey key)
section s = new section(); throws NoSuchPaddingException, InvalidKeyException,
s.ID = buffer.get(); NoSuchAlgorithmException, IllegalBlockSizeException,
BadPaddingException, InvalidAlgorithmParameterException, IOException, JSONException {
int len = buffer.getInt(); byte[] bytes = Base64.decode((String) _content, Base64.NO_WRAP);
s.Data = new byte[len]; CryptResult result = key.decrypt(bytes, _cryptParameters);
buffer.get(s.Data); return new JSONObject(new String(result.Data, "UTF-8"));
return s;
} }
private static void assertLength(byte[] bytes, int length) throws Exception { public void setContent(JSONObject dbObj) {
if (bytes.length != length) { _content = dbObj;
throw new Exception("Bad length");
}
} }
public byte[] getContent() { public void setContent(JSONObject dbObj, MasterKey key)
return _content; throws JSONException, UnsupportedEncodingException,
} NoSuchPaddingException, InvalidKeyException, NoSuchAlgorithmException,
IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException {
String string = dbObj.toString(4);
byte[] dbBytes = string.getBytes("UTF-8");
public void setContent(byte[] content) { CryptResult result = key.encrypt(dbBytes);
_content = content; _content = new String(Base64.encode(result.Data, Base64.NO_WRAP), "UTF-8");
} _cryptParameters = result.Parameters;
public CryptParameters getCryptParameters() {
return _cryptParameters;
}
public void setCryptParameters(CryptParameters parameters) {
_cryptParameters = parameters;
} }
public SlotCollection getSlots() { public SlotCollection getSlots() {
@ -167,9 +120,4 @@ public class DatabaseFile {
public void setSlots(SlotCollection slots) { public void setSlots(SlotCollection slots) {
_slots = slots; _slots = slots;
} }
private static class section {
byte ID;
byte[] Data;
}
} }

View file

@ -3,6 +3,9 @@ package me.impy.aegis.db;
import android.content.Context; import android.content.Context;
import android.os.Environment; import android.os.Environment;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.DataInputStream; import java.io.DataInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
@ -16,9 +19,9 @@ import me.impy.aegis.crypto.CryptResult;
import me.impy.aegis.crypto.MasterKey; import me.impy.aegis.crypto.MasterKey;
public class DatabaseManager { public class DatabaseManager {
private static final String FILENAME = "aegis.db"; private static final String FILENAME = "aegis.json";
private static final String FILENAME_EXPORT = "aegis_export.db"; private static final String FILENAME_EXPORT = "aegis_export.json";
private static final String FILENAME_EXPORT_PLAIN = "aegis_export.json"; private static final String FILENAME_EXPORT_PLAIN = "aegis_export_plain.json";
private MasterKey _key; private MasterKey _key;
private DatabaseFile _file; private DatabaseFile _file;
@ -58,9 +61,9 @@ public class DatabaseManager {
_file.deserialize(fileBytes); _file.deserialize(fileBytes);
if (!_file.isEncrypted()) { if (!_file.isEncrypted()) {
byte[] contentBytes = _file.getContent(); JSONObject obj = _file.getContent();
_db = new Database(); _db = new Database();
_db.deserialize(contentBytes); _db.deserialize(obj);
} }
} }
@ -73,15 +76,13 @@ public class DatabaseManager {
public void unlock(MasterKey key) throws Exception { public void unlock(MasterKey key) throws Exception {
assertState(true, true); assertState(true, true);
byte[] encrypted = _file.getContent(); JSONObject obj = _file.getContent(key);
CryptParameters params = _file.getCryptParameters();
CryptResult result = key.decrypt(encrypted, params);
_db = new Database(); _db = new Database();
_db.deserialize(result.Data); _db.deserialize(obj);
_key = key; _key = key;
} }
public static void save(Context context, DatabaseFile file) throws IOException { public static void save(Context context, DatabaseFile file) throws IOException, JSONException {
byte[] bytes = file.serialize(); byte[] bytes = file.serialize();
FileOutputStream stream = null; FileOutputStream stream = null;
@ -98,26 +99,21 @@ public class DatabaseManager {
public void save() throws Exception { public void save() throws Exception {
assertState(false, true); assertState(false, true);
byte[] dbBytes = _db.serialize(); JSONObject obj = _db.serialize();
if (!_file.isEncrypted()) { if (_file.isEncrypted()) {
_file.setContent(dbBytes); _file.setContent(obj, _key);
} else { } else {
CryptResult result = _key.encrypt(dbBytes); _file.setContent(obj);
_file.setContent(result.Data);
_file.setCryptParameters(result.Parameters);
} }
save(_context, _file); save(_context, _file);
} }
public String export(boolean encrypt) throws Exception { public String export(boolean encrypt) throws Exception {
assertState(false, true); assertState(false, true);
byte[] bytes = _db.serialize(!encrypt); if (encrypt && getFile().isEncrypted()) {
encrypt = encrypt && getFile().isEncrypted(); _file.setContent(_db.serialize(), _key);
if (encrypt) { } else {
CryptResult result = _key.encrypt(bytes); _file.setContent(_db.serialize());
_file.setContent(result.Data);
_file.setCryptParameters(result.Parameters);
bytes = _file.serialize();
} }
File file; File file;
@ -129,6 +125,7 @@ public class DatabaseManager {
throw new IOException("error creating external storage directory"); throw new IOException("error creating external storage directory");
} }
byte[] bytes = _file.serialize();
file = new File(dir.getAbsolutePath(), encrypt ? FILENAME_EXPORT : FILENAME_EXPORT_PLAIN); file = new File(dir.getAbsolutePath(), encrypt ? FILENAME_EXPORT : FILENAME_EXPORT_PLAIN);
stream = new FileOutputStream(file); stream = new FileOutputStream(file);
stream.write(bytes); stream.write(bytes);

View file

@ -1,6 +1,11 @@
package me.impy.aegis.db.slots; package me.impy.aegis.db.slots;
public class FingerprintSlot extends RawSlot { public class FingerprintSlot extends RawSlot {
public FingerprintSlot() {
super();
}
@Override @Override
public byte getType() { public byte getType() {
return TYPE_FINGERPRINT; return TYPE_FINGERPRINT;

View file

@ -1,12 +1,15 @@
package me.impy.aegis.db.slots; package me.impy.aegis.db.slots;
import org.json.JSONException;
import org.json.JSONObject;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException; import java.security.spec.InvalidKeySpecException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import me.impy.aegis.crypto.CryptoUtils; import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.util.LittleByteBuffer; import me.impy.aegis.encoding.Hex;
public class PasswordSlot extends RawSlot { public class PasswordSlot extends RawSlot {
private int _n; private int _n;
@ -19,27 +22,22 @@ public class PasswordSlot extends RawSlot {
} }
@Override @Override
public byte[] serialize() { public JSONObject serialize() throws JSONException {
byte[] bytes = super.serialize(); JSONObject obj = super.serialize();
LittleByteBuffer buffer = LittleByteBuffer.wrap(bytes); obj.put("n", _n);
buffer.position(super.getSize()); obj.put("r", _r);
buffer.putInt(_n); obj.put("p", _p);
buffer.putInt(_r); obj.put("salt", Hex.toString(_salt));
buffer.putInt(_p); return obj;
buffer.put(_salt);
return buffer.array();
} }
@Override @Override
public void deserialize(byte[] data) throws Exception { public void deserialize(JSONObject obj) throws Exception {
super.deserialize(data); super.deserialize(obj);
LittleByteBuffer buffer = LittleByteBuffer.wrap(data); _n = obj.getInt("n");
buffer.position(super.getSize()); _r = obj.getInt("r");
_n = buffer.getInt(); _p = obj.getInt("p");
_r = buffer.getInt(); _salt = Hex.toBytes(obj.getString("salt"));
_p = buffer.getInt();
_salt = new byte[CryptoUtils.CRYPTO_SALT_SIZE];
buffer.get(_salt);
} }
public SecretKey deriveKey(char[] password, byte[] salt, int n, int r, int p) throws InvalidKeySpecException, NoSuchAlgorithmException { public SecretKey deriveKey(char[] password, byte[] salt, int n, int r, int p) throws InvalidKeySpecException, NoSuchAlgorithmException {
@ -55,11 +53,6 @@ public class PasswordSlot extends RawSlot {
return CryptoUtils.deriveKey(password, _salt, _n, _r, _p); return CryptoUtils.deriveKey(password, _salt, _n, _r, _p);
} }
@Override
public int getSize() {
return super.getSize() + /* _n, _r, _p */ 4 + 4 + 4 + CryptoUtils.CRYPTO_SALT_SIZE;
}
@Override @Override
public byte getType() { public byte getType() {
return TYPE_DERIVED; return TYPE_DERIVED;

View file

@ -1,40 +1,11 @@
package me.impy.aegis.db.slots; package me.impy.aegis.db.slots;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.util.LittleByteBuffer;
public class RawSlot extends Slot { public class RawSlot extends Slot {
public RawSlot() { public RawSlot() {
super(); super();
} }
@Override
public byte[] serialize() {
LittleByteBuffer buffer = LittleByteBuffer.allocate(getSize());
buffer.put(getType());
buffer.put(_id);
buffer.put(_encryptedMasterKey);
return buffer.array();
}
@Override
public void deserialize(byte[] data) throws Exception {
LittleByteBuffer buffer = LittleByteBuffer.wrap(data);
if (buffer.get() != getType()) {
throw new Exception("slot type mismatch");
}
_id = new byte[ID_SIZE];
buffer.get(_id);
_encryptedMasterKey = new byte[CryptoUtils.CRYPTO_KEY_SIZE];
buffer.get(_encryptedMasterKey);
}
@Override
public int getSize() {
return 1 + ID_SIZE + CryptoUtils.CRYPTO_KEY_SIZE;
}
@Override @Override
public byte getType() { public byte getType() {
return TYPE_RAW; return TYPE_RAW;

View file

@ -2,6 +2,9 @@ package me.impy.aegis.db.slots;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable; import java.io.Serializable;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
@ -51,14 +54,24 @@ public abstract class Slot implements Serializable {
return cipher; return cipher;
} }
public JSONObject serialize() throws JSONException {
JSONObject obj = new JSONObject();
obj.put("type", getType());
obj.put("id", Hex.toString(_id));
obj.put("key", Hex.toString(_encryptedMasterKey));
return obj;
}
public void deserialize(JSONObject obj) throws Exception {
if (obj.getInt("type") != getType()) {
throw new Exception("slot type mismatch");
}
_id = Hex.toBytes(obj.getString("id"));
_encryptedMasterKey = Hex.toBytes(obj.getString("key"));
}
public abstract byte getType();
public String getID() { public String getID() {
return Hex.toString(_id); return Hex.toString(_id);
} }
public abstract int getSize();
public abstract byte getType();
// a slot has a binary representation
public abstract byte[] serialize();
public abstract void deserialize(byte[] data) throws Exception;
} }

View file

@ -1,5 +1,9 @@
package me.impy.aegis.db.slots; package me.impy.aegis.db.slots;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable; import java.io.Serializable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -9,44 +13,38 @@ import java.util.List;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.crypto.MasterKey; import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.util.LittleByteBuffer; import me.impy.aegis.encoding.Hex;
public class SlotCollection implements Iterable<Slot>, Serializable { public class SlotCollection implements Iterable<Slot>, Serializable {
private List<Slot> _slots = new ArrayList<>(); private List<Slot> _slots = new ArrayList<>();
private byte[] _masterHash; private byte[] _masterHash;
public static byte[] serialize(SlotCollection slots) { public static JSONObject serialize(SlotCollection slots) throws JSONException {
// yep, no streams at this api level JSONObject obj = new JSONObject();
int size = 0; obj.put("hash", Hex.toString(slots.getMasterHash()));
JSONArray entries = new JSONArray();
for (Slot slot : slots) { for (Slot slot : slots) {
size += slot.getSize(); entries.put(slot.serialize());
}
size += CryptoUtils.CRYPTO_HASH_SIZE;
LittleByteBuffer buffer = LittleByteBuffer.allocate(size);
buffer.put(slots.getMasterHash());
for (Slot slot : slots) {
byte[] bytes = slot.serialize();
buffer.put(bytes);
}
return buffer.array();
} }
public static SlotCollection deserialize(byte[] data) throws Exception { obj.put("entries", entries);
LittleByteBuffer buffer = LittleByteBuffer.wrap(data); return obj;
byte[] masterHash = new byte[CryptoUtils.CRYPTO_HASH_SIZE]; }
buffer.get(masterHash);
public static SlotCollection deserialize(JSONObject obj) throws Exception {
SlotCollection slots = new SlotCollection(); SlotCollection slots = new SlotCollection();
byte[] masterHash = Hex.toBytes(obj.getString("hash"));
slots.setMasterHash(masterHash); slots.setMasterHash(masterHash);
while (buffer.remaining() > 0) { JSONArray entries = obj.getJSONArray("entries");
for (int i = 0; i < entries.length(); i++) {
Slot slot; Slot slot;
JSONObject slotObj = entries.getJSONObject(i);
switch (buffer.peek()) { switch (slotObj.getInt("type")) {
case Slot.TYPE_RAW: case Slot.TYPE_RAW:
slot = new RawSlot(); slot = new RawSlot();
break; break;
@ -60,10 +58,7 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
throw new Exception("unrecognized slot type"); throw new Exception("unrecognized slot type");
} }
byte[] bytes = new byte[slot.getSize()]; slot.deserialize(slotObj);
buffer.get(bytes);
slot.deserialize(bytes);
slots.add(slot); slots.add(slot);
} }

View file

@ -15,11 +15,11 @@ public class Hex {
private static final char[] hexCode = "0123456789abcdef".toCharArray(); private static final char[] hexCode = "0123456789abcdef".toCharArray();
public static byte[] toBytes(String s) { public static byte[] toBytes(String s) throws HexException {
final int len = s.length(); final int len = s.length();
if (len % 2 != 0) if (len % 2 != 0)
throw new IllegalArgumentException("hexBinary needs to be even-length: " + s); throw new HexException("hexBinary needs to be even-length: " + s);
byte[] out = new byte[len / 2]; byte[] out = new byte[len / 2];
@ -27,7 +27,7 @@ public class Hex {
int h = hexToBin(s.charAt(i)); int h = hexToBin(s.charAt(i));
int l = hexToBin(s.charAt(i + 1)); int l = hexToBin(s.charAt(i + 1));
if (h == -1 || l == -1) if (h == -1 || l == -1)
throw new IllegalArgumentException("contains illegal character for hexBinary: " + s); throw new HexException("contains illegal character for hexBinary: " + s);
out[i / 2] = (byte) (h * 16 + l); out[i / 2] = (byte) (h * 16 + l);
} }

View file

@ -0,0 +1,7 @@
package me.impy.aegis.encoding;
public class HexException extends Exception {
public HexException(String message) {
super(message);
}
}

View file

@ -4,6 +4,7 @@ import java.util.List;
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.DatabaseFile;
import me.impy.aegis.util.ByteInputStream; import me.impy.aegis.util.ByteInputStream;
public class AegisImporter extends DatabaseImporter { public class AegisImporter extends DatabaseImporter {
@ -15,8 +16,10 @@ public class AegisImporter extends DatabaseImporter {
@Override @Override
public List<DatabaseEntry> convert() throws Exception { public List<DatabaseEntry> convert() throws Exception {
byte[] bytes = _stream.getBytes(); byte[] bytes = _stream.getBytes();
DatabaseFile file = new DatabaseFile();
file.deserialize(bytes);
Database db = new Database(); Database db = new Database();
db.deserialize(bytes, false); db.deserialize(file.getContent());
return db.getKeys(); return db.getKeys();
} }

View file

@ -9,6 +9,8 @@ import com.github.paolorotolo.appintro.AppIntro;
import com.github.paolorotolo.appintro.AppIntroFragment; import com.github.paolorotolo.appintro.AppIntroFragment;
import com.github.paolorotolo.appintro.model.SliderPage; import com.github.paolorotolo.appintro.model.SliderPage;
import org.json.JSONObject;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
@ -164,7 +166,7 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
try { try {
// encrypt the master key with the fingerprint key // encrypt the master key with the fingerprint key
// and add it to the list of slots // and add it to the list of slots
FingerprintSlot slot = new FingerprintSlot(); FingerprintSlot slot = _authenticatedSlide.getFingerSlot();
Cipher cipher = _authenticatedSlide.getFingerCipher(); Cipher cipher = _authenticatedSlide.getFingerCipher();
slots.encrypt(slot, masterKey, cipher); slots.encrypt(slot, masterKey, cipher);
slots.add(slot); slots.add(slot);
@ -176,13 +178,11 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
// finally, save the database // finally, save the database
try { try {
byte[] bytes = _database.serialize(); JSONObject obj = _database.serialize();
if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) { if (cryptType == CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
_databaseFile.setContent(bytes); _databaseFile.setContent(obj);
} else { } else {
CryptResult result = masterKey.encrypt(bytes); _databaseFile.setContent(obj, masterKey);
_databaseFile.setContent(result.Data);
_databaseFile.setCryptParameters(result.Parameters);
} }
DatabaseManager.save(getApplicationContext(), _databaseFile); DatabaseManager.save(getApplicationContext(), _databaseFile);
} catch (Exception e) { } catch (Exception e) {

View file

@ -41,7 +41,7 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
private TextView _textFingerprint; private TextView _textFingerprint;
private FingerprintUiHelper _fingerHelper; private FingerprintUiHelper _fingerHelper;
private KeyStoreHandle _storeHandle; private KeyStoreHandle _storeHandle;
private FingerprintSlot _slot; private FingerprintSlot _fingerSlot;
private Cipher _fingerCipher; private Cipher _fingerCipher;
private boolean _fingerAuthenticated; private boolean _fingerAuthenticated;
@ -75,6 +75,10 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
return _fingerCipher; return _fingerCipher;
} }
public FingerprintSlot getFingerSlot() {
return _fingerSlot;
}
public void setBgColor(int color) { public void setBgColor(int color) {
_bgColor = color; _bgColor = color;
} }
@ -95,9 +99,9 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
try { try {
if (_storeHandle == null) { if (_storeHandle == null) {
_storeHandle = new KeyStoreHandle(); _storeHandle = new KeyStoreHandle();
_slot = new FingerprintSlot(); _fingerSlot = new FingerprintSlot();
} }
key = _storeHandle.generateKey(_slot.getID()); key = _storeHandle.generateKey(_fingerSlot.getID());
} catch (Exception e) { } catch (Exception e) {
throw new UndeclaredThrowableException(e); throw new UndeclaredThrowableException(e);
} }

View file

@ -1,38 +0,0 @@
package me.impy.aegis.util;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
// LittleByteBuffer wraps a ByteBuffer to extend its API a little.
// Its byte order is set to little endian by default.
// All this boilerplate just to change the default byte order and add a peek method... Is it worth it? Probably not.
public class LittleByteBuffer {
private ByteBuffer _buffer;
private LittleByteBuffer(ByteBuffer buffer) {
_buffer = buffer;
_buffer.order(ByteOrder.LITTLE_ENDIAN);
}
public byte peek() {
_buffer.mark();
byte b = _buffer.get();
_buffer.reset();
return b;
}
public byte get() { return _buffer.get(); }
public LittleByteBuffer get(byte[] dst) {_buffer.get(dst); return this; }
public LittleByteBuffer put(byte b) { _buffer.put(b); return this; }
public LittleByteBuffer put(byte[] bytes) { _buffer.put(bytes); return this; }
public int remaining() { return _buffer.remaining(); }
public byte[] array() { return _buffer.array(); }
public LittleByteBuffer putInt(int i) { _buffer.putInt(i); return this; }
public LittleByteBuffer putLong(long l) { _buffer.putLong(l); return this; }
public int getInt() { return _buffer.getInt(); }
public long getLong() { return _buffer.getLong(); }
public int position() { return _buffer.position(); }
public LittleByteBuffer position(int i) { _buffer.position(i); return this; }
public static LittleByteBuffer allocate(int size) { return new LittleByteBuffer(ByteBuffer.allocate(size)); }
public static LittleByteBuffer wrap(byte[] bytes) { return new LittleByteBuffer(ByteBuffer.wrap(bytes)); }
}