Use GCM instead of a hash to check master key integrity

This is the last database format change before the initial release, probably
This commit is contained in:
Alexander Bakker 2018-06-06 19:38:13 +02:00
parent da37b5175e
commit c3f94b37c8
21 changed files with 211 additions and 214 deletions

View file

@ -1,6 +1,36 @@
package me.impy.aegis.crypto;
public class CryptParameters {
import org.json.JSONException;
import org.json.JSONObject;
import java.io.Serializable;
import me.impy.aegis.encoding.Hex;
import me.impy.aegis.encoding.HexException;
public class CryptParameters implements Serializable {
public byte[] Nonce;
public byte[] Tag;
public JSONObject toJson() {
JSONObject obj = new JSONObject();
try {
obj.put("nonce", Hex.encode(Nonce));
obj.put("tag", Hex.encode(Tag));
} catch (JSONException e) {
throw new RuntimeException(e);
}
return obj;
}
public static CryptParameters parseJson(JSONObject obj) throws JSONException, HexException {
byte[] tag = Hex.decode(obj.getString("tag"));
byte[] nonce = Hex.decode(obj.getString("nonce"));
return new CryptParameters() {{
Tag = tag;
Nonce = nonce;
}};
}
}

View file

@ -2,16 +2,13 @@ package me.impy.aegis.crypto;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.lang.reflect.UndeclaredThrowableException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.InvalidKeySpecException;
import java.util.Arrays;
import javax.crypto.BadPaddingException;
@ -20,20 +17,16 @@ import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.spongycastle.crypto.generators.SCrypt;
public class CryptoUtils {
public static final String CRYPTO_HASH = "SHA-256";
public static final String CRYPTO_CIPHER_RAW = "AES/ECB/NoPadding";
public static final byte CRYPTO_KEY_SIZE = 32;
public static final String CRYPTO_CIPHER_AEAD = "AES/GCM/NoPadding";
public static final byte CRYPTO_TAG_SIZE = 16;
public static final byte CRYPTO_NONCE_SIZE = 12;
public static final String CRYPTO_AEAD = "AES/GCM/NoPadding";
public static final byte CRYPTO_AEAD_KEY_SIZE = 32;
public static final byte CRYPTO_AEAD_TAG_SIZE = 16;
public static final byte CRYPTO_AEAD_NONCE_SIZE = 12;
public static final int CRYPTO_SCRYPT_N = 1 << 15;
public static final int CRYPTO_SCRYPT_r = 8;
@ -41,23 +34,36 @@ public class CryptoUtils {
public static SecretKey deriveKey(char[] password, byte[] salt, int n, int r, int p) {
byte[] bytes = toBytes(password);
byte[] keyBytes = SCrypt.generate(bytes, salt, n, r, p, CRYPTO_KEY_SIZE);
byte[] keyBytes = SCrypt.generate(bytes, salt, n, r, p, CRYPTO_AEAD_KEY_SIZE);
return new SecretKeySpec(keyBytes, 0, keyBytes.length, "AES");
}
public static Cipher createCipher(SecretKey key, int opmode)
public static Cipher createEncryptCipher(SecretKey key)
throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException {
byte[] nonce = generateNonce();
return createCipher(key, opmode, nonce);
return createCipher(key, Cipher.ENCRYPT_MODE, null);
}
public static Cipher createCipher(SecretKey key, int opmode, byte[] nonce)
public static Cipher createDecryptCipher(SecretKey key, byte[] nonce)
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException,
InvalidKeyException, NoSuchPaddingException {
return createCipher(key, Cipher.DECRYPT_MODE, nonce);
}
private static Cipher createCipher(SecretKey key, int opmode, byte[] nonce)
throws NoSuchPaddingException, NoSuchAlgorithmException,
InvalidAlgorithmParameterException, InvalidKeyException {
IvParameterSpec spec = new IvParameterSpec(nonce);
Cipher cipher = Cipher.getInstance(CRYPTO_CIPHER_AEAD);
cipher.init(opmode, key, spec);
Cipher cipher = Cipher.getInstance(CRYPTO_AEAD);
// generate the nonce if none is given
// we are not allowed to do this ourselves as "setRandomizedEncryptionRequired" is set to true
if (nonce != null) {
GCMParameterSpec spec = new GCMParameterSpec(CRYPTO_AEAD_TAG_SIZE * 8, nonce);
cipher.init(opmode, key, spec);
} else {
cipher.init(opmode, key);
}
return cipher;
}
@ -65,8 +71,8 @@ public class CryptoUtils {
throws BadPaddingException, IllegalBlockSizeException {
// split off the tag to store it separately
byte[] result = cipher.doFinal(data);
byte[] tag = Arrays.copyOfRange(result, result.length - CRYPTO_TAG_SIZE, result.length);
byte[] encrypted = Arrays.copyOfRange(result, 0, result.length - CRYPTO_TAG_SIZE);
byte[] tag = Arrays.copyOfRange(result, result.length - CRYPTO_AEAD_TAG_SIZE, result.length);
byte[] encrypted = Arrays.copyOfRange(result, 0, result.length - CRYPTO_AEAD_TAG_SIZE);
return new CryptResult() {{
Parameters = new CryptParameters() {{
@ -93,23 +99,10 @@ public class CryptoUtils {
}};
}
public static byte[] hashKey(SecretKey key) {
MessageDigest hash;
try {
hash = MessageDigest.getInstance(CRYPTO_HASH);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
byte[] bytes = key.getEncoded();
hash.update(bytes);
return hash.digest();
}
public static SecretKey generateKey() {
try {
KeyGenerator generator = KeyGenerator.getInstance("AES");
generator.init(CRYPTO_KEY_SIZE * 8);
generator.init(CRYPTO_AEAD_KEY_SIZE * 8);
return generator.generateKey();
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
@ -117,11 +110,7 @@ public class CryptoUtils {
}
public static byte[] generateSalt() {
return generateRandomBytes(CRYPTO_KEY_SIZE);
}
public static byte[] generateNonce() {
return generateRandomBytes(CRYPTO_NONCE_SIZE);
return generateRandomBytes(CRYPTO_AEAD_KEY_SIZE);
}
public static byte[] generateRandomBytes(int length) {

View file

@ -1,6 +1,5 @@
package me.impy.aegis.crypto;
import android.annotation.SuppressLint;
import android.os.Build;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
@ -51,11 +50,11 @@ public class KeyStoreHandle {
KeyGenerator generator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, STORE_NAME);
generator.init(new KeyGenParameterSpec.Builder(id,
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
.setBlockModes(KeyProperties.BLOCK_MODE_ECB)
.setBlockModes(KeyProperties.BLOCK_MODE_GCM)
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
.setUserAuthenticationRequired(true)
.setRandomizedEncryptionRequired(false)
.setKeySize(CryptoUtils.CRYPTO_KEY_SIZE * 8)
.setRandomizedEncryptionRequired(true)
.setKeySize(CryptoUtils.CRYPTO_AEAD_KEY_SIZE * 8)
.build());
return generator.generateKey();
@ -81,8 +80,7 @@ public class KeyStoreHandle {
// and see if KeyPermanentlyInvalidatedException is thrown
if (isSupported()) {
try {
@SuppressLint("GetInstance")
Cipher cipher = Cipher.getInstance(CryptoUtils.CRYPTO_CIPHER_RAW);
Cipher cipher = Cipher.getInstance(CryptoUtils.CRYPTO_AEAD);
cipher.init(Cipher.ENCRYPT_MODE, key);
} catch (KeyPermanentlyInvalidatedException e) {
return null;

View file

@ -28,7 +28,7 @@ public class MasterKey implements Serializable {
public CryptResult encrypt(byte[] bytes) throws MasterKeyException {
try {
Cipher cipher = CryptoUtils.createCipher(_key, Cipher.ENCRYPT_MODE);
Cipher cipher = CryptoUtils.createEncryptCipher(_key);
return CryptoUtils.encrypt(bytes, cipher);
} catch (NoSuchPaddingException
| NoSuchAlgorithmException
@ -42,7 +42,7 @@ public class MasterKey implements Serializable {
public CryptResult decrypt(byte[] bytes, CryptParameters params) throws MasterKeyException {
try {
Cipher cipher = CryptoUtils.createCipher(_key, Cipher.DECRYPT_MODE, params.Nonce);
Cipher cipher = CryptoUtils.createDecryptCipher(_key, params.Nonce);
return CryptoUtils.decrypt(bytes, cipher, params);
} catch (NoSuchPaddingException
| NoSuchAlgorithmException
@ -55,10 +55,6 @@ public class MasterKey implements Serializable {
}
}
public byte[] getHash() {
return CryptoUtils.hashKey(_key);
}
public byte[] getBytes() {
return _key.getEncoded();
}

View file

@ -1,5 +1,6 @@
package me.impy.aegis.db;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@ -9,11 +10,10 @@ 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.SlotCollection;
import me.impy.aegis.db.slots.SlotCollectionException;
import me.impy.aegis.db.slots.SlotList;
import me.impy.aegis.db.slots.SlotListException;
import me.impy.aegis.encoding.Base64;
import me.impy.aegis.encoding.Base64Exception;
import me.impy.aegis.encoding.Hex;
import me.impy.aegis.encoding.HexException;
public class DatabaseFile {
@ -21,22 +21,15 @@ public class DatabaseFile {
private Object _content;
private CryptParameters _cryptParameters;
private SlotCollection _slots;
private SlotList _slots;
public byte[] serialize() {
try {
JSONObject cryptObj = null;
if (isEncrypted()) {
cryptObj = new JSONObject();
cryptObj.put("nonce", Hex.encode(_cryptParameters.Nonce));
cryptObj.put("tag", Hex.encode(_cryptParameters.Tag));
}
// don't write the crypt parameters if the content is not encrypted
boolean plain = _content instanceof JSONObject || _slots == null || cryptObj == null;
// 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 : SlotCollection.serialize(_slots));
headerObj.put("params", plain ? JSONObject.NULL : cryptObj);
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);
@ -58,17 +51,14 @@ public class DatabaseFile {
throw new DatabaseFileException("unsupported version");
}
JSONObject slotObj = headerObj.optJSONObject("slots");
JSONArray slotObj = headerObj.optJSONArray("slots");
if (slotObj != null) {
_slots = SlotCollection.deserialize(slotObj);
_slots = SlotList.deserialize(slotObj);
}
JSONObject cryptObj = headerObj.optJSONObject("params");
if (cryptObj != null) {
_cryptParameters = new CryptParameters() {{
Nonce = Hex.decode(cryptObj.getString("nonce"));
Tag = Hex.decode(cryptObj.getString("tag"));
}};
_cryptParameters = CryptParameters.parseJson(cryptObj);
}
if (cryptObj == null || slotObj == null) {
@ -76,7 +66,7 @@ public class DatabaseFile {
} else {
_content = obj.getString("db");
}
} catch (SlotCollectionException | UnsupportedEncodingException | JSONException | HexException e) {
} catch (SlotListException | UnsupportedEncodingException | JSONException | HexException e) {
throw new DatabaseFileException(e);
}
}
@ -118,11 +108,11 @@ public class DatabaseFile {
}
}
public SlotCollection getSlots() {
public SlotList getSlots() {
return _slots;
}
public void setSlots(SlotCollection slots) {
public void setSlots(SlotList slots) {
_slots = slots;
}
}

View file

@ -14,7 +14,7 @@ import java.util.List;
import me.impy.aegis.BuildConfig;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.db.slots.SlotCollection;
import me.impy.aegis.db.slots.SlotList;
public class DatabaseManager {
private static final String FILENAME = "aegis.json";
@ -196,7 +196,7 @@ public class DatabaseManager {
return _file;
}
public void enableEncryption(MasterKey key, SlotCollection slots) {
public void enableEncryption(MasterKey key, SlotList slots) {
assertState(false, true);
_key = key;
_file.setSlots(slots);

View file

@ -1,15 +1,16 @@
package me.impy.aegis.db.slots;
import android.annotation.SuppressLint;
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.AEADBadTagException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
@ -17,6 +18,8 @@ import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import me.impy.aegis.crypto.CryptParameters;
import me.impy.aegis.crypto.CryptResult;
import me.impy.aegis.crypto.CryptoUtils;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.encoding.Hex;
@ -29,40 +32,55 @@ public abstract class Slot implements Serializable {
protected UUID _uuid;
protected byte[] _encryptedMasterKey;
protected CryptParameters _encryptedMasterKeyParams;
protected Slot() {
_uuid = UUID.randomUUID();
}
// getKey decrypts the encrypted master key in this slot with the given key and returns it.
public SecretKey getKey(Cipher cipher) throws SlotException {
// 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 {
byte[] decryptedKeyBytes = cipher.doFinal(_encryptedMasterKey);
return new SecretKeySpec(decryptedKeyBytes, CryptoUtils.CRYPTO_CIPHER_AEAD);
} catch (BadPaddingException | IllegalBlockSizeException e) {
CryptResult res = CryptoUtils.decrypt(_encryptedMasterKey, cipher, _encryptedMasterKeyParams);
SecretKey key = new SecretKeySpec(res.Data, CryptoUtils.CRYPTO_AEAD);
return new MasterKey(key);
} catch (AEADBadTagException e) {
throw new SlotIntegrityException(e);
} catch (IOException | BadPaddingException | IllegalBlockSizeException e) {
throw new SlotException(e);
}
}
// setKey encrypts the given master key with the given key and stores the result in this slot.
// 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();
_encryptedMasterKey = cipher.doFinal(masterKeyBytes);
CryptResult res = CryptoUtils.encrypt(masterKeyBytes, cipher);
_encryptedMasterKey = res.Data;
_encryptedMasterKeyParams = res.Parameters;
} catch (BadPaddingException | IllegalBlockSizeException e) {
throw new SlotException(e);
}
}
// suppress the AES ECB warning
// this is perfectly safe because we discard this cipher after passing CryptoUtils.CRYPTO_KEY_SIZE bytes through it
@SuppressLint("getInstance")
public static Cipher createCipher(SecretKey key, int mode) throws SlotException {
public static Cipher createEncryptCipher(SecretKey key) throws SlotException {
try {
Cipher cipher = Cipher.getInstance(CryptoUtils.CRYPTO_CIPHER_RAW);
cipher.init(mode, key);
return cipher;
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException e) {
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.Nonce);
} catch (InvalidAlgorithmParameterException
| NoSuchAlgorithmException
| InvalidKeyException
| NoSuchPaddingException e) {
throw new SlotException(e);
}
}
@ -70,9 +88,11 @@ public abstract class Slot implements Serializable {
public JSONObject serialize() {
try {
JSONObject obj = new JSONObject();
JSONObject paramObj = _encryptedMasterKeyParams.toJson();
obj.put("type", getType());
obj.put("uuid", _uuid.toString());
obj.put("key", Hex.encode(_encryptedMasterKey));
obj.put("key_params", paramObj);
return obj;
} catch (JSONException e) {
throw new RuntimeException(e);
@ -84,13 +104,17 @@ public abstract class Slot implements Serializable {
if (obj.getInt("type") != getType()) {
throw new SlotException("slot type mismatch");
}
// if there is no uuid, generate a new one
if (!obj.has("uuid")) {
_uuid = UUID.randomUUID();
} else {
_uuid = UUID.fromString(obj.getString("uuid"));
}
JSONObject paramObj = obj.getJSONObject("key_params");
_encryptedMasterKey = Hex.decode(obj.getString("key"));
_encryptedMasterKeyParams = CryptParameters.parseJson(paramObj);
} catch (JSONException | HexException e) {
throw new SlotException(e);
}

View file

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

View file

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

View file

@ -6,47 +6,28 @@ import org.json.JSONObject;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import javax.crypto.Cipher;
import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.encoding.Hex;
import me.impy.aegis.encoding.HexException;
public class SlotCollection implements Iterable<Slot>, Serializable {
public class SlotList implements Iterable<Slot>, Serializable {
private List<Slot> _slots = new ArrayList<>();
private byte[] _masterHash;
public static JSONObject serialize(SlotCollection slots) {
try {
JSONObject obj = new JSONObject();
obj.put("hash", Hex.encode(slots.getMasterHash()));
JSONArray entries = new JSONArray();
for (Slot slot : slots) {
entries.put(slot.serialize());
}
obj.put("entries", entries);
return obj;
} catch (JSONException e) {
throw new RuntimeException(e);
public static JSONArray serialize(SlotList slots) {
JSONArray array = new JSONArray();
for (Slot slot : slots) {
array.put(slot.serialize());
}
return array;
}
public static SlotCollection deserialize(JSONObject obj) throws SlotCollectionException {
SlotCollection slots = new SlotCollection();
public static SlotList deserialize(JSONArray array) throws SlotListException {
SlotList slots = new SlotList();
try {
byte[] masterHash = Hex.decode(obj.getString("hash"));
slots.setMasterHash(masterHash);
JSONArray entries = obj.getJSONArray("entries");
for (int i = 0; i < entries.length(); i++) {
for (int i = 0; i < array.length(); i++) {
Slot slot;
JSONObject slotObj = entries.getJSONObject(i);
JSONObject slotObj = array.getJSONObject(i);
switch (slotObj.getInt("type")) {
case Slot.TYPE_RAW:
@ -65,8 +46,8 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
slot.deserialize(slotObj);
slots.add(slot);
}
} catch (SlotException | JSONException | HexException e) {
throw new SlotCollectionException(e);
} catch (SlotException | JSONException e) {
throw new SlotListException(e);
}
return slots;
@ -116,26 +97,4 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
public Iterator<Slot> iterator() {
return _slots.iterator();
}
public void encrypt(Slot slot, MasterKey key, Cipher cipher) throws SlotException {
slot.setKey(key, cipher);
setMasterHash(key.getHash());
}
public MasterKey decrypt(Slot slot, Cipher cipher) throws SlotException, SlotIntegrityException {
byte[] hash = getMasterHash();
MasterKey key = new MasterKey(slot.getKey(cipher));
if (!Arrays.equals(hash, key.getHash())) {
throw new SlotIntegrityException();
}
return key;
}
private void setMasterHash(byte[] masterHash) {
_masterHash = masterHash;
}
private byte[] getMasterHash() {
return _masterHash;
}
}

View file

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

View file

@ -26,7 +26,7 @@ import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.db.slots.FingerprintSlot;
import me.impy.aegis.db.slots.PasswordSlot;
import me.impy.aegis.db.slots.Slot;
import me.impy.aegis.db.slots.SlotCollection;
import me.impy.aegis.db.slots.SlotList;
import me.impy.aegis.db.slots.SlotException;
import me.impy.aegis.helpers.FingerprintHelper;
import me.impy.aegis.helpers.FingerprintUiHelper;
@ -36,7 +36,7 @@ import me.impy.aegis.ui.tasks.SlotCollectionTask;
public class AuthActivity extends AegisActivity implements FingerprintUiHelper.Callback, SlotCollectionTask.Callback {
private EditText _textPassword;
private SlotCollection _slots;
private SlotList _slots;
private FingerprintUiHelper _fingerHelper;
private Cipher _fingerCipher;
@ -57,7 +57,7 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C
}
Intent intent = getIntent();
_slots = (SlotCollection) intent.getSerializableExtra("slots");
_slots = (SlotList) intent.getSerializableExtra("slots");
// only show the fingerprint controls if the api version is new enough, permission is granted, a scanner is found and a fingerprint slot is found
FingerprintManager manager = FingerprintHelper.getManager(this);
@ -75,7 +75,7 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C
invalidated = true;
continue;
}
_fingerCipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
_fingerCipher = slot.createDecryptCipher(key);
_fingerHelper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this);
boxFingerprint.setVisibility(View.VISIBLE);
invalidated = false;

View file

@ -23,7 +23,7 @@ import me.impy.aegis.db.DatabaseManagerException;
import me.impy.aegis.db.slots.FingerprintSlot;
import me.impy.aegis.db.slots.PasswordSlot;
import me.impy.aegis.db.slots.Slot;
import me.impy.aegis.db.slots.SlotCollection;
import me.impy.aegis.db.slots.SlotList;
import me.impy.aegis.db.Database;
import me.impy.aegis.db.DatabaseFile;
import me.impy.aegis.db.DatabaseManager;
@ -145,7 +145,7 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
masterKey = MasterKey.generate();
}
SlotCollection slots = null;
SlotList slots = null;
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
// encrypt the master key with a key derived from the user's password
// and add it to the list of slots
@ -153,8 +153,8 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
throw new RuntimeException();
}
try {
slots = new SlotCollection();
slots.encrypt(_passwordSlot, masterKey, _passwordCipher);
_passwordSlot.setKey(masterKey, _passwordCipher);
slots = new SlotList();
slots.add(_passwordSlot);
_databaseFile.setSlots(slots);
} catch (SlotException e) {
@ -168,7 +168,7 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
// and add it to the list of slots
FingerprintSlot slot = _authenticatedSlide.getFingerSlot();
Cipher cipher = _authenticatedSlide.getFingerCipher();
slots.encrypt(slot, masterKey, cipher);
slot.setKey(masterKey, cipher);
slots.add(slot);
} catch (SlotException e) {
setException(e);
@ -204,7 +204,7 @@ public class IntroActivity extends AppIntro implements DerivationTask.Callback {
public void onTaskFinished(SecretKey key) {
if (key != null) {
try {
_passwordCipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
_passwordCipher = Slot.createEncryptCipher(key);
} catch (SlotException e) {
setException(e);
}

View file

@ -32,7 +32,7 @@ import me.impy.aegis.db.DatabaseEntry;
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.SlotCollection;
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;
@ -369,7 +369,7 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
return;
}
SlotCollection slots = (SlotCollection) data.getSerializableExtra("slots");
SlotList slots = (SlotList) data.getSerializableExtra("slots");
_db.getFile().setSlots(slots);
saveDatabase();
}
@ -390,9 +390,9 @@ public class PreferencesFragment extends PreferenceFragmentCompat implements Pas
public void onSlotResult(Slot slot, Cipher cipher) {
MasterKey masterKey = MasterKey.generate();
SlotCollection slots = new SlotCollection();
SlotList slots = new SlotList();
try {
slots.encrypt(slot, masterKey, cipher);
slot.setKey(masterKey, cipher);
} catch (SlotException e) {
onException(e);
return;

View file

@ -20,7 +20,7 @@ import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.db.slots.FingerprintSlot;
import me.impy.aegis.db.slots.PasswordSlot;
import me.impy.aegis.db.slots.Slot;
import me.impy.aegis.db.slots.SlotCollection;
import me.impy.aegis.db.slots.SlotList;
import me.impy.aegis.db.slots.SlotException;
import me.impy.aegis.helpers.FingerprintHelper;
import me.impy.aegis.ui.dialogs.Dialogs;
@ -31,7 +31,7 @@ import me.impy.aegis.ui.dialogs.SlotDialogFragment;
public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Listener, SlotDialogFragment.Listener {
private MasterKey _masterKey;
private SlotCollection _slots;
private SlotList _slots;
private SlotAdapter _adapter;
private boolean _edited = false;
@ -65,7 +65,7 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
// load the slots and masterKey
_masterKey = (MasterKey) getIntent().getSerializableExtra("masterKey");
_slots = (SlotCollection) getIntent().getSerializableExtra("slots");
_slots = (SlotList) getIntent().getSerializableExtra("slots");
for (Slot slot : _slots) {
_adapter.addSlot(slot);
}
@ -176,7 +176,7 @@ public class SlotManagerActivity extends AegisActivity implements SlotAdapter.Li
@Override
public void onSlotResult(Slot slot, Cipher cipher) {
try {
_slots.encrypt(slot, _masterKey, cipher);
slot.setKey(_masterKey, cipher);
} catch (SlotException e) {
onException(e);
return;

View file

@ -38,7 +38,7 @@ public class FingerprintDialogFragment extends SlotDialogFragment implements Fin
try {
_slot = new FingerprintSlot();
SecretKey key = new KeyStoreHandle().generateKey(_slot.getUUID().toString());
_cipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
_cipher = Slot.createEncryptCipher(key);
_helper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this);
} catch (KeyStoreHandleException | SlotException e) {
throw new RuntimeException(e);

View file

@ -51,7 +51,7 @@ public class PasswordDialogFragment extends SlotDialogFragment {
DerivationTask task = new DerivationTask(getActivity(), key -> {
Cipher cipher;
try {
cipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
cipher = Slot.createEncryptCipher(key);
} catch (SlotException e) {
getListener().onException(e);
dialog.cancel();

View file

@ -113,7 +113,7 @@ public class CustomAuthenticatedSlide extends Fragment implements FingerprintUiH
}
try {
_fingerCipher = Slot.createCipher(key, Cipher.ENCRYPT_MODE);
_fingerCipher = Slot.createEncryptCipher(key);
} catch (Exception e) {
throw new UndeclaredThrowableException(e);
}

View file

@ -2,8 +2,6 @@ package me.impy.aegis.ui.tasks;
import android.content.Context;
import java.lang.reflect.UndeclaredThrowableException;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
@ -11,7 +9,7 @@ import me.impy.aegis.crypto.MasterKey;
import me.impy.aegis.db.slots.FingerprintSlot;
import me.impy.aegis.db.slots.PasswordSlot;
import me.impy.aegis.db.slots.Slot;
import me.impy.aegis.db.slots.SlotCollection;
import me.impy.aegis.db.slots.SlotList;
import me.impy.aegis.db.slots.SlotException;
import me.impy.aegis.db.slots.SlotIntegrityException;
@ -41,15 +39,17 @@ public class SlotCollectionTask<T extends Slot> extends ProgressDialogTask<SlotC
if (slot instanceof PasswordSlot) {
char[] password = (char[])params.Obj;
SecretKey key = ((PasswordSlot)slot).deriveKey(password);
Cipher cipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
masterKey = params.Slots.decrypt(slot, cipher);
Cipher cipher = slot.createDecryptCipher(key);
masterKey = slot.getKey(cipher);
} else if (slot instanceof FingerprintSlot) {
masterKey = params.Slots.decrypt(slot, (Cipher)params.Obj);
masterKey = slot.getKey((Cipher)params.Obj);
} else {
throw new RuntimeException();
}
break;
} catch (SlotIntegrityException e) { }
} catch (SlotIntegrityException e) {
}
}
if (masterKey == null) {
@ -60,7 +60,7 @@ public class SlotCollectionTask<T extends Slot> extends ProgressDialogTask<SlotC
} catch (SlotIntegrityException e) {
return null;
} catch (SlotException e) {
throw new UndeclaredThrowableException(e);
throw new RuntimeException(e);
}
}
@ -71,7 +71,7 @@ public class SlotCollectionTask<T extends Slot> extends ProgressDialogTask<SlotC
}
public static class Params {
public SlotCollection Slots;
public SlotList Slots;
public Object Obj;
}

File diff suppressed because one or more lines are too long

View file

@ -52,7 +52,7 @@
"secret": "6CAIGVYB5MQ6TSZLJ56HJBWU5S3H7FUC",
"algo": "SHA512",
"digits": 6,
"counter": 30
"counter": 96
}
},
{
@ -100,7 +100,7 @@
"secret": "EIQMT7NHFYJUMBKQ35P34JGLG3MO7L2W",
"algo": "SHA1",
"digits": 8,
"counter": 20
"counter": 30
}
},
{