Clean up integrity checking code. Try every available slot, not just the first one.

This commit is contained in:
Alexander Bakker 2017-08-19 13:50:33 +02:00
parent 7269cc2b6a
commit 6e5962600e
7 changed files with 110 additions and 58 deletions

View file

@ -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

View file

@ -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);

View file

@ -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();
}
}

View file

@ -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);
}

View file

@ -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;
}
}

View file

@ -0,0 +1,5 @@
package me.impy.aegis.crypto.slots;
public class SlotIntegrityException extends Exception {
}

View file

@ -34,5 +34,4 @@ public class AuthHelper {
CryptoUtils.zero(passwordConfirm);
return equal;
}
}