mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-06-04 05:40:21 +00:00
Clean up integrity checking code. Try every available slot, not just the first one.
This commit is contained in:
parent
7269cc2b6a
commit
6e5962600e
7 changed files with 110 additions and 58 deletions
|
@ -19,9 +19,14 @@ import android.widget.LinearLayout;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import java.lang.reflect.UndeclaredThrowableException;
|
import java.lang.reflect.UndeclaredThrowableException;
|
||||||
import java.util.Arrays;
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
import me.impy.aegis.crypto.CryptoUtils;
|
import me.impy.aegis.crypto.CryptoUtils;
|
||||||
|
@ -31,6 +36,7 @@ import me.impy.aegis.crypto.slots.FingerprintSlot;
|
||||||
import me.impy.aegis.crypto.slots.PasswordSlot;
|
import me.impy.aegis.crypto.slots.PasswordSlot;
|
||||||
import me.impy.aegis.crypto.slots.Slot;
|
import me.impy.aegis.crypto.slots.Slot;
|
||||||
import me.impy.aegis.crypto.slots.SlotCollection;
|
import me.impy.aegis.crypto.slots.SlotCollection;
|
||||||
|
import me.impy.aegis.crypto.slots.SlotIntegrityException;
|
||||||
import me.impy.aegis.finger.FingerprintUiHelper;
|
import me.impy.aegis.finger.FingerprintUiHelper;
|
||||||
import me.impy.aegis.helpers.AuthHelper;
|
import me.impy.aegis.helpers.AuthHelper;
|
||||||
|
|
||||||
|
@ -84,43 +90,70 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
||||||
button.setOnClickListener(new View.OnClickListener() {
|
button.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
MasterKey masterKey = null;
|
trySlots(PasswordSlot.class);
|
||||||
try {
|
|
||||||
if (slots.has(PasswordSlot.class)) {
|
|
||||||
PasswordSlot slot = slots.find(PasswordSlot.class);
|
|
||||||
char[] password = AuthHelper.getPassword(textPassword, true);
|
|
||||||
SecretKey key = slot.deriveKey(password);
|
|
||||||
CryptoUtils.zero(password);
|
|
||||||
Cipher cipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
|
|
||||||
masterKey = MasterKey.decryptSlot(slot, cipher);
|
|
||||||
} else {
|
|
||||||
throw new RuntimeException();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// TODO: feedback
|
|
||||||
throw new UndeclaredThrowableException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
setKey(masterKey);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setKey(MasterKey key) {
|
private void showError() {
|
||||||
if (!Arrays.equals(slots.getMasterHash(), key.getHash())) {
|
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
builder.setMessage("Master key integrity check failed for every slot. Make sure you didn't mistype your password.");
|
||||||
builder.setMessage("Unable to decrypt the master key with the given key.");
|
builder.setCancelable(false);
|
||||||
builder.setCancelable(false);
|
builder.setPositiveButton("OK",
|
||||||
builder.setPositiveButton("OK",
|
|
||||||
new DialogInterface.OnClickListener() {
|
new DialogInterface.OnClickListener() {
|
||||||
public void onClick(DialogInterface dialog, int id) {
|
public void onClick(DialogInterface dialog, int id) {
|
||||||
dialog.cancel();
|
dialog.cancel();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
builder.create().show();
|
builder.create().show();
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
private MasterKey decryptPasswordSlot(PasswordSlot slot) throws Exception {
|
||||||
|
char[] password = AuthHelper.getPassword(textPassword, true);
|
||||||
|
SecretKey key = slot.deriveKey(password);
|
||||||
|
CryptoUtils.zero(password);
|
||||||
|
Cipher cipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
|
||||||
|
return slots.decrypt(slot, cipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
private MasterKey decryptFingerSlot(FingerprintSlot slot) throws Exception {
|
||||||
|
return slots.decrypt(slot, fingerCipher);
|
||||||
|
}
|
||||||
|
|
||||||
|
private <T extends Slot> void trySlots(Class<T> type) {
|
||||||
|
try {
|
||||||
|
if (!slots.has(type)) {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
MasterKey masterKey = null;
|
||||||
|
for (Slot slot : slots.findAll(type)) {
|
||||||
|
try {
|
||||||
|
if (slot instanceof PasswordSlot) {
|
||||||
|
masterKey = decryptPasswordSlot((PasswordSlot) slot);
|
||||||
|
} else if (slot instanceof FingerprintSlot) {
|
||||||
|
masterKey = decryptFingerSlot((FingerprintSlot) slot);
|
||||||
|
} else {
|
||||||
|
throw new RuntimeException();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
} catch (SlotIntegrityException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (masterKey == null) {
|
||||||
|
throw new SlotIntegrityException();
|
||||||
|
}
|
||||||
|
|
||||||
|
setKey(masterKey);
|
||||||
|
} catch (SlotIntegrityException e) {
|
||||||
|
showError();
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new UndeclaredThrowableException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setKey(MasterKey key) {
|
||||||
// send the master key back to the main activity
|
// send the master key back to the main activity
|
||||||
Intent result = new Intent();
|
Intent result = new Intent();
|
||||||
result.putExtra("key", key);
|
result.putExtra("key", key);
|
||||||
|
@ -153,15 +186,7 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAuthenticated() {
|
public void onAuthenticated() {
|
||||||
MasterKey key;
|
trySlots(FingerprintSlot.class);
|
||||||
FingerprintSlot slot = slots.find(FingerprintSlot.class);
|
|
||||||
try {
|
|
||||||
key = new MasterKey(slot.getKey(fingerCipher));
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new UndeclaredThrowableException(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
setKey(key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -109,15 +109,13 @@ public class IntroActivity extends AppIntro {
|
||||||
}
|
}
|
||||||
|
|
||||||
SlotCollection slots = databaseFile.getSlots();
|
SlotCollection slots = databaseFile.getSlots();
|
||||||
slots.setMasterHash(masterKey.getHash());
|
|
||||||
|
|
||||||
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
||||||
try {
|
try {
|
||||||
// encrypt the master key with a key derived from the user's password
|
// encrypt the master key with a key derived from the user's password
|
||||||
// and add it to the list of slots
|
// and add it to the list of slots
|
||||||
PasswordSlot slot = new PasswordSlot();
|
PasswordSlot slot = new PasswordSlot();
|
||||||
Cipher cipher = authenticatedSlide.getCipher(slot);
|
Cipher cipher = authenticatedSlide.getCipher(slot);
|
||||||
masterKey.encryptSlot(slot, cipher);
|
slots.encrypt(slot, masterKey, cipher);
|
||||||
slots.add(slot);
|
slots.add(slot);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
setException(e);
|
setException(e);
|
||||||
|
@ -131,7 +129,7 @@ public class IntroActivity extends AppIntro {
|
||||||
// and add it to the list of slots
|
// and add it to the list of slots
|
||||||
FingerprintSlot slot = new FingerprintSlot();
|
FingerprintSlot slot = new FingerprintSlot();
|
||||||
Cipher cipher = authenticatedSlide.getCipher(slot);
|
Cipher cipher = authenticatedSlide.getCipher(slot);
|
||||||
masterKey.encryptSlot(slot, cipher);
|
slots.encrypt(slot, masterKey, cipher);
|
||||||
slots.add(slot);
|
slots.add(slot);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
setException(e);
|
setException(e);
|
||||||
|
|
|
@ -12,8 +12,6 @@ import javax.crypto.IllegalBlockSizeException;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
import me.impy.aegis.crypto.slots.Slot;
|
|
||||||
|
|
||||||
public class MasterKey implements Serializable {
|
public class MasterKey implements Serializable {
|
||||||
private SecretKey _key;
|
private SecretKey _key;
|
||||||
|
|
||||||
|
@ -28,16 +26,6 @@ public class MasterKey implements Serializable {
|
||||||
return new MasterKey(CryptoUtils.generateKey());
|
return new MasterKey(CryptoUtils.generateKey());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void encryptSlot(Slot slot, Cipher cipher)
|
|
||||||
throws BadPaddingException, IllegalBlockSizeException {
|
|
||||||
slot.setKey(_key, cipher);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MasterKey decryptSlot(Slot slot, Cipher cipher)
|
|
||||||
throws BadPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException {
|
|
||||||
return new MasterKey(slot.getKey(cipher));
|
|
||||||
}
|
|
||||||
|
|
||||||
public CryptResult encrypt(byte[] bytes)
|
public CryptResult encrypt(byte[] bytes)
|
||||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException,
|
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException,
|
||||||
NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {
|
NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {
|
||||||
|
@ -55,4 +43,8 @@ public class MasterKey implements Serializable {
|
||||||
public byte[] getHash() {
|
public byte[] getHash() {
|
||||||
return CryptoUtils.hashKey(_key);
|
return CryptoUtils.hashKey(_key);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getBytes() {
|
||||||
|
return _key.getEncoded();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
import me.impy.aegis.crypto.CryptoUtils;
|
import me.impy.aegis.crypto.CryptoUtils;
|
||||||
|
import me.impy.aegis.crypto.MasterKey;
|
||||||
|
|
||||||
public abstract class Slot implements Serializable {
|
public abstract class Slot implements Serializable {
|
||||||
public final static byte TYPE_RAW = 0x00;
|
public final static byte TYPE_RAW = 0x00;
|
||||||
|
@ -31,8 +32,8 @@ public abstract class Slot implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
// setKey encrypts the given master key with the given key and stores the result in this slot.
|
// setKey encrypts the given master key with the given key and stores the result in this slot.
|
||||||
public void setKey(SecretKey masterKey, Cipher cipher) throws BadPaddingException, IllegalBlockSizeException {
|
public void setKey(MasterKey masterKey, Cipher cipher) throws BadPaddingException, IllegalBlockSizeException {
|
||||||
byte[] masterKeyBytes = masterKey.getEncoded();
|
byte[] masterKeyBytes = masterKey.getBytes();
|
||||||
_encryptedMasterKey = cipher.doFinal(masterKeyBytes);
|
_encryptedMasterKey = cipher.doFinal(masterKeyBytes);
|
||||||
CryptoUtils.zero(masterKeyBytes);
|
CryptoUtils.zero(masterKeyBytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,19 @@
|
||||||
package me.impy.aegis.crypto.slots;
|
package me.impy.aegis.crypto.slots;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.crypto.BadPaddingException;
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
import javax.crypto.SecretKey;
|
import javax.crypto.SecretKey;
|
||||||
|
|
||||||
import me.impy.aegis.crypto.CryptoUtils;
|
import me.impy.aegis.crypto.CryptoUtils;
|
||||||
|
import me.impy.aegis.crypto.MasterKey;
|
||||||
import me.impy.aegis.util.LittleByteBuffer;
|
import me.impy.aegis.util.LittleByteBuffer;
|
||||||
|
|
||||||
public class SlotCollection implements Iterable<Slot>, Serializable {
|
public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
|
@ -88,6 +94,16 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
return null;
|
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) {
|
public <T extends Slot> boolean has(Class<T> type) {
|
||||||
return find(type) != null;
|
return find(type) != null;
|
||||||
}
|
}
|
||||||
|
@ -97,11 +113,27 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||||
return _slots.iterator();
|
return _slots.iterator();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMasterHash(byte[] masterHash) {
|
public void encrypt(Slot slot, MasterKey key, Cipher cipher)
|
||||||
|
throws BadPaddingException, IllegalBlockSizeException {
|
||||||
|
slot.setKey(key, cipher);
|
||||||
|
setMasterHash(key.getHash());
|
||||||
|
}
|
||||||
|
|
||||||
|
public MasterKey decrypt(Slot slot, Cipher cipher)
|
||||||
|
throws SlotIntegrityException, BadPaddingException, IllegalBlockSizeException {
|
||||||
|
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;
|
_masterHash = masterHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getMasterHash() {
|
private byte[] getMasterHash() {
|
||||||
return _masterHash;
|
return _masterHash;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
package me.impy.aegis.crypto.slots;
|
||||||
|
|
||||||
|
public class SlotIntegrityException extends Exception {
|
||||||
|
|
||||||
|
}
|
|
@ -34,5 +34,4 @@ public class AuthHelper {
|
||||||
CryptoUtils.zero(passwordConfirm);
|
CryptoUtils.zero(passwordConfirm);
|
||||||
return equal;
|
return equal;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue