Rename package to com.beemdevelopment.aegis

This commit is contained in:
Alexander Bakker 2019-02-07 22:39:33 +01:00
parent ceb03de240
commit 62425511a1
101 changed files with 366 additions and 362 deletions

View file

@ -0,0 +1,77 @@
package com.beemdevelopment.aegis.db;
import com.beemdevelopment.aegis.encoding.Base64Exception;
import com.beemdevelopment.aegis.otp.OtpInfoException;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.List;
import java.util.UUID;
public class Database {
private static final int VERSION = 1;
private DatabaseEntryList _entries = new DatabaseEntryList();
public JSONObject toJson() {
try {
JSONArray array = new JSONArray();
for (DatabaseEntry e : _entries) {
array.put(e.toJson());
}
JSONObject obj = new JSONObject();
obj.put("version", VERSION);
obj.put("entries", array);
return obj;
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public static Database fromJson(JSONObject obj) throws DatabaseException {
Database db = new Database();
try {
int ver = obj.getInt("version");
if (ver != VERSION) {
throw new DatabaseException("Unsupported version");
}
JSONArray array = obj.getJSONArray("entries");
for (int i = 0; i < array.length(); i++) {
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) {
_entries.add(entry);
}
public void removeEntry(DatabaseEntry entry) {
_entries.remove(entry);
}
public void replaceEntry(DatabaseEntry newEntry) {
_entries.replace(newEntry);
}
public void swapEntries(DatabaseEntry entry1, DatabaseEntry entry2) {
_entries.swap(entry1, entry2);
}
public List<DatabaseEntry> getEntries() {
return _entries.getList();
}
public DatabaseEntry getEntryByUUID(UUID uuid) {
return _entries.getByUUID(uuid);
}
}

View file

@ -0,0 +1,154 @@
package com.beemdevelopment.aegis.db;
import com.beemdevelopment.aegis.encoding.Base64;
import com.beemdevelopment.aegis.encoding.Base64Exception;
import com.beemdevelopment.aegis.otp.GoogleAuthInfo;
import com.beemdevelopment.aegis.otp.OtpInfo;
import com.beemdevelopment.aegis.otp.OtpInfoException;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable;
import java.util.Arrays;
import java.util.Objects;
import java.util.UUID;
public class DatabaseEntry implements Serializable {
private UUID _uuid;
private String _name = "";
private String _issuer = "";
private String _group;
private OtpInfo _info;
private byte[] _icon;
private DatabaseEntry(UUID uuid, OtpInfo info) {
_uuid = uuid;
_info = info;
}
public DatabaseEntry(OtpInfo info) {
this(UUID.randomUUID(), info);
}
public DatabaseEntry(OtpInfo info, String name, String issuer) {
this(info);
setName(name);
setIssuer(issuer);
}
public DatabaseEntry(GoogleAuthInfo info) {
this(info.getOtpInfo(), info.getAccountName(), info.getIssuer());
}
public JSONObject toJson() {
JSONObject obj = new JSONObject();
try {
obj.put("type", _info.getType());
obj.put("uuid", _uuid.toString());
obj.put("name", _name);
obj.put("issuer", _issuer);
obj.put("group", _group);
obj.put("icon", _icon == null ? JSONObject.NULL : Base64.encode(_icon));
obj.put("info", _info.toJson());
} catch (JSONException e) {
throw new RuntimeException(e);
}
return obj;
}
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();
} else {
uuid = UUID.fromString(obj.getString("uuid"));
}
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"));
entry.setGroup(obj.optString("group", null));
Object icon = obj.get("icon");
if (icon != JSONObject.NULL) {
entry.setIcon(Base64.decode((String) icon));
}
return entry;
}
public void resetUUID() {
_uuid = UUID.randomUUID();
}
public UUID getUUID() {
return _uuid;
}
public String getName() {
return _name;
}
public String getIssuer() {
return _issuer;
}
public String getGroup() {
return _group;
}
public byte[] getIcon() {
return _icon;
}
public OtpInfo getInfo() {
return _info;
}
public void setName(String name) {
_name = name;
}
public void setIssuer(String issuer) {
_issuer = issuer;
}
public void setGroup(String group) {
_group = group;
}
public void setInfo(OtpInfo info) {
_info = info;
}
public void setIcon(byte[] icon) {
_icon = icon;
}
public boolean hasIcon() {
return _icon != null;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof DatabaseEntry)) {
return false;
}
DatabaseEntry entry = (DatabaseEntry) o;
return getUUID().equals(entry.getUUID())
&& getName().equals(entry.getName())
&& getIssuer().equals(entry.getIssuer())
&& Objects.equals(getGroup(), entry.getGroup())
&& getInfo().equals(entry.getInfo())
&& Arrays.equals(getIcon(), entry.getIcon());
}
}

View file

@ -0,0 +1,62 @@
package com.beemdevelopment.aegis.db;
import androidx.annotation.NonNull;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.UUID;
public class DatabaseEntryList implements Iterable<DatabaseEntry>, Serializable {
private List<DatabaseEntry> _entries = new ArrayList<>();
@NonNull
@Override
public Iterator<DatabaseEntry> iterator() {
return _entries.iterator();
}
public void add(DatabaseEntry entry) {
if (getByUUID(entry.getUUID()) != null) {
throw new AssertionError("entry found with the same uuid");
}
_entries.add(entry);
}
public void remove(DatabaseEntry entry) {
entry = mustGetByUUID(entry.getUUID());
_entries.remove(entry);
}
public void replace(DatabaseEntry newEntry) {
DatabaseEntry oldEntry = mustGetByUUID(newEntry.getUUID());
_entries.set(_entries.indexOf(oldEntry), newEntry);
}
public void swap(DatabaseEntry entry1, DatabaseEntry entry2) {
Collections.swap(_entries, _entries.indexOf(entry1), _entries.indexOf(entry2));
}
public List<DatabaseEntry> getList() {
return Collections.unmodifiableList(_entries);
}
public DatabaseEntry getByUUID(UUID uuid) {
for (DatabaseEntry entry : _entries) {
if (entry.getUUID().equals(uuid)) {
return entry;
}
}
return null;
}
private DatabaseEntry mustGetByUUID(UUID uuid) {
DatabaseEntry entry = getByUUID(uuid);
if (entry == null) {
throw new AssertionError("no entry found with the same uuid");
}
return entry;
}
}

View file

@ -0,0 +1,11 @@
package com.beemdevelopment.aegis.db;
public class DatabaseException extends Exception {
public DatabaseException(Throwable cause) {
super(cause);
}
public DatabaseException(String message) {
super(message);
}
}

View file

@ -0,0 +1,167 @@
package com.beemdevelopment.aegis.db;
import com.beemdevelopment.aegis.crypto.CryptParameters;
import com.beemdevelopment.aegis.crypto.CryptResult;
import com.beemdevelopment.aegis.crypto.MasterKeyException;
import com.beemdevelopment.aegis.db.slots.SlotList;
import com.beemdevelopment.aegis.db.slots.SlotListException;
import com.beemdevelopment.aegis.encoding.Base64;
import com.beemdevelopment.aegis.encoding.Base64Exception;
import com.beemdevelopment.aegis.encoding.HexException;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.UnsupportedEncodingException;
public class DatabaseFile {
public static final byte VERSION = 1;
private Object _content;
private Header _header;
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 {
JSONObject obj = new JSONObject();
obj.put("version", VERSION);
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) {
throw new RuntimeException(e);
}
}
public static DatabaseFile fromJson(JSONObject obj) throws DatabaseFileException {
try {
if (obj.getInt("version") > VERSION) {
throw new DatabaseFileException("unsupported version");
}
Header header = Header.fromJson(obj.getJSONObject("header"));
if (!header.isEmpty()) {
return new DatabaseFile(obj.getString("db"), header);
}
return new DatabaseFile(obj.getJSONObject("db"), header);
} catch (JSONException e) {
throw new DatabaseFileException(e);
}
}
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(DatabaseFileCredentials creds) throws DatabaseFileException {
try {
byte[] bytes = Base64.decode((String) _content);
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 obj) {
_content = obj;
_header = new Header(null, null);
}
public void setContent(JSONObject obj, DatabaseFileCredentials creds) throws DatabaseFileException {
try {
String string = obj.toString(4);
byte[] dbBytes = string.getBytes("UTF-8");
CryptResult result = creds.encrypt(dbBytes);
_content = Base64.encode(result.getData());
_header = new Header(creds.getSlots(), result.getParams());
} catch (MasterKeyException | UnsupportedEncodingException | JSONException e) {
throw new DatabaseFileException(e);
}
}
public static class Header {
private SlotList _slots;
private CryptParameters _params;
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;
}
}
}

View file

@ -0,0 +1,41 @@
package com.beemdevelopment.aegis.db;
import com.beemdevelopment.aegis.crypto.CryptParameters;
import com.beemdevelopment.aegis.crypto.CryptResult;
import com.beemdevelopment.aegis.crypto.MasterKey;
import com.beemdevelopment.aegis.crypto.MasterKeyException;
import java.io.Serializable;
import com.beemdevelopment.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;
}
}

View file

@ -0,0 +1,11 @@
package com.beemdevelopment.aegis.db;
public class DatabaseFileException extends Exception {
public DatabaseFileException(Throwable cause) {
super(cause);
}
public DatabaseFileException(String message) {
super(message);
}
}

View file

@ -0,0 +1,236 @@
package com.beemdevelopment.aegis.db;
import android.content.Context;
import android.os.Environment;
import org.json.JSONObject;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.Collator;
import java.util.List;
import java.util.TreeSet;
import java.util.UUID;
import com.beemdevelopment.aegis.BuildConfig;
import com.beemdevelopment.aegis.R;
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 Database _db;
private DatabaseFile _file;
private DatabaseFileCredentials _creds;
private boolean _encrypt;
private Context _context;
public DatabaseManager(Context context) {
_context = context;
}
public boolean fileExists() {
File file = new File(_context.getFilesDir(), FILENAME);
return file.exists() && file.isFile();
}
public void load() throws DatabaseManagerException {
assertState(true, false);
try (FileInputStream file = _context.openFileInput(FILENAME)) {
byte[] fileBytes = new byte[(int) file.getChannel().size()];
DataInputStream stream = new DataInputStream(file);
stream.readFully(fileBytes);
stream.close();
_file = DatabaseFile.fromBytes(fileBytes);
_encrypt = _file.isEncrypted();
if (!isEncryptionEnabled()) {
JSONObject obj = _file.getContent();
_db = Database.fromJson(obj);
}
} catch (IOException | DatabaseFileException | DatabaseException e) {
throw new DatabaseManagerException(e);
}
}
public void lock() {
assertState(false, true);
_creds = null;
_db = null;
}
public void unlock(DatabaseFileCredentials creds) throws DatabaseManagerException {
assertState(true, true);
try {
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 {
byte[] bytes = file.toBytes();
try (FileOutputStream stream = context.openFileOutput(FILENAME, Context.MODE_PRIVATE)) {
stream.write(bytes);
} catch (IOException e) {
throw new DatabaseManagerException(e);
}
}
public void save() throws DatabaseManagerException {
assertState(false, true);
try {
JSONObject obj = _db.toJson();
if (isEncryptionEnabled()) {
_file.setContent(obj, _creds);
} else {
_file.setContent(obj);
}
save(_context, _file);
} catch (DatabaseFileException e) {
throw new DatabaseManagerException(e);
}
}
public String export(boolean encrypt) throws DatabaseManagerException {
assertState(false, true);
try {
DatabaseFile dbFile = new DatabaseFile();
if (encrypt && isEncryptionEnabled()) {
dbFile.setContent(_db.toJson(), _creds);
} else {
dbFile.setContent(_db.toJson());
}
String dirName = !BuildConfig.DEBUG ? _context.getString(R.string.app_name) : _context.getString(R.string.app_name_dev);
File dir = new File(Environment.getExternalStorageDirectory(), dirName);
if (!dir.exists() && !dir.mkdirs()) {
throw new IOException("error creating external storage directory");
}
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);
}
return file.getAbsolutePath();
} catch (IOException | DatabaseFileException e) {
throw new DatabaseManagerException(e);
}
}
public void addEntry(DatabaseEntry entry) {
assertState(false, true);
_db.addEntry(entry);
}
public void removeEntry(DatabaseEntry entry) {
assertState(false, true);
_db.removeEntry(entry);
}
public void replaceEntry(DatabaseEntry entry) {
assertState(false, true);
_db.replaceEntry(entry);
}
public void swapEntries(DatabaseEntry entry1, DatabaseEntry entry2) {
assertState(false, true);
_db.swapEntries(entry1, entry2);
}
public List<DatabaseEntry> getEntries() {
assertState(false, true);
return _db.getEntries();
}
public DatabaseEntry getEntryByUUID(UUID uuid) {
assertState(false, true);
return _db.getEntryByUUID(uuid);
}
public TreeSet<String> getGroups() {
assertState(false, true);
TreeSet<String> groups = new TreeSet<>(Collator.getInstance());
for (DatabaseEntry entry : getEntries()) {
String group = entry.getGroup();
if (group != null) {
groups.add(group);
}
}
return groups;
}
public DatabaseFileCredentials getCredentials() {
assertState(false, true);
return _creds;
}
public void setCredentials(DatabaseFileCredentials creds) {
assertState(false, true);
_creds = creds;
}
public DatabaseFile.Header getFileHeader() {
assertLoaded(true);
return _file.getHeader();
}
public boolean isEncryptionEnabled() {
assertLoaded(true);
return _encrypt;
}
public void enableEncryption(DatabaseFileCredentials creds) throws DatabaseManagerException {
assertState(false, true);
_creds = creds;
_encrypt = true;
save();
}
public void disableEncryption() throws DatabaseManagerException {
assertState(false, true);
_creds = null;
_encrypt = false;
save();
}
public boolean isLoaded() {
return _file != null;
}
public boolean isLocked() {
return _db == null;
}
private void assertState(boolean locked, boolean 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 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");
}
}
}

View file

@ -0,0 +1,7 @@
package com.beemdevelopment.aegis.db;
public class DatabaseManagerException extends Exception {
public DatabaseManagerException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,20 @@
package com.beemdevelopment.aegis.db.slots;
import com.beemdevelopment.aegis.crypto.CryptParameters;
import java.util.UUID;
public class FingerprintSlot extends RawSlot {
public FingerprintSlot() {
super();
}
FingerprintSlot(UUID uuid, byte[] key, CryptParameters keyParams) {
super(uuid, key, keyParams);
}
@Override
public byte getType() {
return TYPE_FINGERPRINT;
}
}

View file

@ -0,0 +1,55 @@
package com.beemdevelopment.aegis.db.slots;
import com.beemdevelopment.aegis.crypto.CryptParameters;
import com.beemdevelopment.aegis.crypto.CryptoUtils;
import com.beemdevelopment.aegis.crypto.SCryptParameters;
import com.beemdevelopment.aegis.encoding.Hex;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.UUID;
import javax.crypto.SecretKey;
public class PasswordSlot extends RawSlot {
private SCryptParameters _params;
public PasswordSlot() {
super();
}
protected PasswordSlot(UUID uuid, byte[] key, CryptParameters keyParams, SCryptParameters scryptParams) {
super(uuid, key, keyParams);
_params = scryptParams;
}
@Override
public JSONObject toJson() {
try {
JSONObject obj = super.toJson();
obj.put("n", _params.getN());
obj.put("r", _params.getR());
obj.put("p", _params.getP());
obj.put("salt", Hex.encode(_params.getSalt()));
return obj;
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public SecretKey deriveKey(char[] password, SCryptParameters params) {
SecretKey key = CryptoUtils.deriveKey(password, params);
_params = params;
return key;
}
public SecretKey deriveKey(char[] password) {
return CryptoUtils.deriveKey(password, _params);
}
@Override
public byte getType() {
return TYPE_DERIVED;
}
}

View file

@ -0,0 +1,20 @@
package com.beemdevelopment.aegis.db.slots;
import com.beemdevelopment.aegis.crypto.CryptParameters;
import java.util.UUID;
public class RawSlot extends Slot {
public RawSlot() {
super();
}
protected RawSlot(UUID uuid, byte[] key, CryptParameters keyParams) {
super(uuid, key, keyParams);
}
@Override
public byte getType() {
return TYPE_RAW;
}
}

View file

@ -0,0 +1,152 @@
package com.beemdevelopment.aegis.db.slots;
import com.beemdevelopment.aegis.crypto.CryptParameters;
import com.beemdevelopment.aegis.crypto.CryptResult;
import com.beemdevelopment.aegis.crypto.CryptoUtils;
import com.beemdevelopment.aegis.crypto.MasterKey;
import com.beemdevelopment.aegis.crypto.SCryptParameters;
import com.beemdevelopment.aegis.encoding.Hex;
import com.beemdevelopment.aegis.encoding.HexException;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.IOException;
import java.io.Serializable;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public abstract class Slot implements Serializable {
public final static byte TYPE_RAW = 0x00;
public final static byte TYPE_DERIVED = 0x01;
public final static byte TYPE_FINGERPRINT = 0x02;
private UUID _uuid;
private byte[] _encryptedMasterKey;
private CryptParameters _encryptedMasterKeyParams;
protected Slot() {
_uuid = UUID.randomUUID();
}
protected Slot(UUID uuid, byte[] key, CryptParameters keyParams) {
_uuid = uuid;
_encryptedMasterKey = key;
_encryptedMasterKeyParams = keyParams;
}
// getKey decrypts the encrypted master key in this slot using the given cipher and returns it.
public MasterKey getKey(Cipher cipher) throws SlotException, SlotIntegrityException {
try {
CryptResult res = CryptoUtils.decrypt(_encryptedMasterKey, cipher, _encryptedMasterKeyParams);
SecretKey key = new SecretKeySpec(res.getData(), CryptoUtils.CRYPTO_AEAD);
return new MasterKey(key);
} catch (BadPaddingException e) {
throw new SlotIntegrityException(e);
} catch (IOException | IllegalBlockSizeException e) {
throw new SlotException(e);
}
}
// setKey encrypts the given master key using the given cipher and stores the result in this slot.
public void setKey(MasterKey masterKey, Cipher cipher) throws SlotException {
try {
byte[] masterKeyBytes = masterKey.getBytes();
CryptResult res = CryptoUtils.encrypt(masterKeyBytes, cipher);
_encryptedMasterKey = res.getData();
_encryptedMasterKeyParams = res.getParams();
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new SlotException(e);
}
}
public static Cipher createEncryptCipher(SecretKey key) throws SlotException {
try {
return CryptoUtils.createEncryptCipher(key);
} catch (InvalidAlgorithmParameterException
| NoSuchPaddingException
| NoSuchAlgorithmException
| InvalidKeyException e) {
throw new SlotException(e);
}
}
public Cipher createDecryptCipher(SecretKey key) throws SlotException {
try {
return CryptoUtils.createDecryptCipher(key, _encryptedMasterKeyParams.getNonce());
} catch (InvalidAlgorithmParameterException
| NoSuchAlgorithmException
| InvalidKeyException
| NoSuchPaddingException e) {
throw new SlotException(e);
}
}
public JSONObject toJson() {
try {
JSONObject obj = new JSONObject();
obj.put("type", getType());
obj.put("uuid", _uuid.toString());
obj.put("key", Hex.encode(_encryptedMasterKey));
obj.put("key_params", _encryptedMasterKeyParams.toJson());
return obj;
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public static Slot fromJson(JSONObject obj) throws SlotException {
Slot slot;
try {
UUID uuid;
if (!obj.has("uuid")) {
uuid = UUID.randomUUID();
} else {
uuid = UUID.fromString(obj.getString("uuid"));
}
byte[] key = Hex.decode(obj.getString("key"));
CryptParameters keyParams = CryptParameters.fromJson(obj.getJSONObject("key_params"));
switch (obj.getInt("type")) {
case Slot.TYPE_RAW:
slot = new RawSlot(uuid, key, keyParams);
break;
case Slot.TYPE_DERIVED:
SCryptParameters scryptParams = new SCryptParameters(
obj.getInt("n"),
obj.getInt("r"),
obj.getInt("p"),
Hex.decode(obj.getString("salt"))
);
slot = new PasswordSlot(uuid, key, keyParams, scryptParams);
break;
case Slot.TYPE_FINGERPRINT:
slot = new FingerprintSlot(uuid, key, keyParams);
break;
default:
throw new SlotException("unrecognized slot type");
}
} catch (JSONException | HexException e) {
throw new SlotException(e);
}
return slot;
}
public abstract byte getType();
public UUID getUUID() {
return _uuid;
}
}

View file

@ -0,0 +1,11 @@
package com.beemdevelopment.aegis.db.slots;
public class SlotException extends Exception {
public SlotException(Throwable cause) {
super(cause);
}
public SlotException(String message) {
super(message);
}
}

View file

@ -0,0 +1,11 @@
package com.beemdevelopment.aegis.db.slots;
public class SlotIntegrityException extends Exception {
public SlotIntegrityException() {
}
public SlotIntegrityException(Throwable cause) {
super(cause);
}
}

View file

@ -0,0 +1,84 @@
package com.beemdevelopment.aegis.db.slots;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SlotList implements Iterable<Slot>, Serializable {
private List<Slot> _slots = new ArrayList<>();
public JSONArray toJson() {
JSONArray array = new JSONArray();
for (Slot slot : this) {
array.put(slot.toJson());
}
return array;
}
public static SlotList fromJson(JSONArray array) throws SlotListException {
SlotList slots = new SlotList();
try {
for (int i = 0; i < array.length(); i++) {
JSONObject obj = array.getJSONObject(i);
Slot slot = Slot.fromJson(obj);
slots.add(slot);
}
} catch (SlotException | JSONException e) {
throw new SlotListException(e);
}
return slots;
}
public void add(Slot slot) {
for (Slot s : this) {
if (s.getUUID().equals(slot.getUUID())) {
throw new AssertionError("slot found with the same uuid");
}
}
_slots.add(slot);
}
public void remove(Slot slot) {
_slots.remove(slot);
}
public int size() {
return _slots.size();
}
public <T extends Slot> T find(Class<T> type) {
for (Slot slot : this) {
if (slot.getClass() == type) {
return type.cast(slot);
}
}
return null;
}
public <T extends Slot> List<T> findAll(Class<T> type) {
ArrayList<T> list = new ArrayList<>();
for (Slot slot : this) {
if (slot.getClass() == type) {
list.add(type.cast(slot));
}
}
return list;
}
public <T extends Slot> boolean has(Class<T> type) {
return find(type) != null;
}
@Override
public Iterator<Slot> iterator() {
return _slots.iterator();
}
}

View file

@ -0,0 +1,11 @@
package com.beemdevelopment.aegis.db.slots;
public class SlotListException extends Exception {
public SlotListException(Throwable cause) {
super(cause);
}
public SlotListException(String message) {
super(message);
}
}