mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-05-27 04:07:15 +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 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.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
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.Slot;
|
||||
import me.impy.aegis.crypto.slots.SlotCollection;
|
||||
import me.impy.aegis.crypto.slots.SlotIntegrityException;
|
||||
import me.impy.aegis.finger.FingerprintUiHelper;
|
||||
import me.impy.aegis.helpers.AuthHelper;
|
||||
|
||||
|
@ -84,43 +90,70 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
|||
button.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
MasterKey masterKey = null;
|
||||
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);
|
||||
trySlots(PasswordSlot.class);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setKey(MasterKey key) {
|
||||
if (!Arrays.equals(slots.getMasterHash(), key.getHash())) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setMessage("Unable to decrypt the master key with the given key.");
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton("OK",
|
||||
private void showError() {
|
||||
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.setCancelable(false);
|
||||
builder.setPositiveButton("OK",
|
||||
new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface dialog, int id) {
|
||||
dialog.cancel();
|
||||
}
|
||||
});
|
||||
builder.create().show();
|
||||
return;
|
||||
}
|
||||
builder.create().show();
|
||||
}
|
||||
|
||||
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
|
||||
Intent result = new Intent();
|
||||
result.putExtra("key", key);
|
||||
|
@ -153,15 +186,7 @@ public class AuthActivity extends AppCompatActivity implements FingerprintUiHelp
|
|||
|
||||
@Override
|
||||
public void onAuthenticated() {
|
||||
MasterKey key;
|
||||
FingerprintSlot slot = slots.find(FingerprintSlot.class);
|
||||
try {
|
||||
key = new MasterKey(slot.getKey(fingerCipher));
|
||||
} catch (Exception e) {
|
||||
throw new UndeclaredThrowableException(e);
|
||||
}
|
||||
|
||||
setKey(key);
|
||||
trySlots(FingerprintSlot.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -109,15 +109,13 @@ public class IntroActivity extends AppIntro {
|
|||
}
|
||||
|
||||
SlotCollection slots = databaseFile.getSlots();
|
||||
slots.setMasterHash(masterKey.getHash());
|
||||
|
||||
if (cryptType != CustomAuthenticationSlide.CRYPT_TYPE_NONE) {
|
||||
try {
|
||||
// encrypt the master key with a key derived from the user's password
|
||||
// and add it to the list of slots
|
||||
PasswordSlot slot = new PasswordSlot();
|
||||
Cipher cipher = authenticatedSlide.getCipher(slot);
|
||||
masterKey.encryptSlot(slot, cipher);
|
||||
slots.encrypt(slot, masterKey, cipher);
|
||||
slots.add(slot);
|
||||
} catch (Exception e) {
|
||||
setException(e);
|
||||
|
@ -131,7 +129,7 @@ public class IntroActivity extends AppIntro {
|
|||
// and add it to the list of slots
|
||||
FingerprintSlot slot = new FingerprintSlot();
|
||||
Cipher cipher = authenticatedSlide.getCipher(slot);
|
||||
masterKey.encryptSlot(slot, cipher);
|
||||
slots.encrypt(slot, masterKey, cipher);
|
||||
slots.add(slot);
|
||||
} catch (Exception e) {
|
||||
setException(e);
|
||||
|
|
|
@ -12,8 +12,6 @@ import javax.crypto.IllegalBlockSizeException;
|
|||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import me.impy.aegis.crypto.slots.Slot;
|
||||
|
||||
public class MasterKey implements Serializable {
|
||||
private SecretKey _key;
|
||||
|
||||
|
@ -28,16 +26,6 @@ public class MasterKey implements Serializable {
|
|||
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)
|
||||
throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, InvalidKeyException,
|
||||
NoSuchPaddingException, BadPaddingException, IllegalBlockSizeException {
|
||||
|
@ -55,4 +43,8 @@ public class MasterKey implements Serializable {
|
|||
public byte[] getHash() {
|
||||
return CryptoUtils.hashKey(_key);
|
||||
}
|
||||
|
||||
public byte[] getBytes() {
|
||||
return _key.getEncoded();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ import javax.crypto.SecretKey;
|
|||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import me.impy.aegis.crypto.CryptoUtils;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
|
||||
public abstract class Slot implements Serializable {
|
||||
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.
|
||||
public void setKey(SecretKey masterKey, Cipher cipher) throws BadPaddingException, IllegalBlockSizeException {
|
||||
byte[] masterKeyBytes = masterKey.getEncoded();
|
||||
public void setKey(MasterKey masterKey, Cipher cipher) throws BadPaddingException, IllegalBlockSizeException {
|
||||
byte[] masterKeyBytes = masterKey.getBytes();
|
||||
_encryptedMasterKey = cipher.doFinal(masterKeyBytes);
|
||||
CryptoUtils.zero(masterKeyBytes);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,19 @@
|
|||
package me.impy.aegis.crypto.slots;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import me.impy.aegis.crypto.CryptoUtils;
|
||||
import me.impy.aegis.crypto.MasterKey;
|
||||
import me.impy.aegis.util.LittleByteBuffer;
|
||||
|
||||
public class SlotCollection implements Iterable<Slot>, Serializable {
|
||||
|
@ -88,6 +94,16 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
|||
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;
|
||||
}
|
||||
|
@ -97,11 +113,27 @@ public class SlotCollection implements Iterable<Slot>, Serializable {
|
|||
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;
|
||||
}
|
||||
|
||||
public byte[] getMasterHash() {
|
||||
private byte[] getMasterHash() {
|
||||
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);
|
||||
return equal;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue