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

View file

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

View file

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

View file

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

View file

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

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); CryptoUtils.zero(passwordConfirm);
return equal; return equal;
} }
} }