From 6e5962600e23adfd13436f7381610fd4f600ce6f Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Sat, 19 Aug 2017 13:50:33 +0200 Subject: [PATCH] Clean up integrity checking code. Try every available slot, not just the first one. --- .../main/java/me/impy/aegis/AuthActivity.java | 99 ++++++++++++------- .../java/me/impy/aegis/IntroActivity.java | 6 +- .../java/me/impy/aegis/crypto/MasterKey.java | 16 +-- .../java/me/impy/aegis/crypto/slots/Slot.java | 5 +- .../aegis/crypto/slots/SlotCollection.java | 36 ++++++- .../crypto/slots/SlotIntegrityException.java | 5 + .../me/impy/aegis/helpers/AuthHelper.java | 1 - 7 files changed, 110 insertions(+), 58 deletions(-) create mode 100644 app/src/main/java/me/impy/aegis/crypto/slots/SlotIntegrityException.java diff --git a/app/src/main/java/me/impy/aegis/AuthActivity.java b/app/src/main/java/me/impy/aegis/AuthActivity.java index 5045855d..2dccc3a0 100644 --- a/app/src/main/java/me/impy/aegis/AuthActivity.java +++ b/app/src/main/java/me/impy/aegis/AuthActivity.java @@ -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 void trySlots(Class 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 diff --git a/app/src/main/java/me/impy/aegis/IntroActivity.java b/app/src/main/java/me/impy/aegis/IntroActivity.java index f2918a86..269d87db 100644 --- a/app/src/main/java/me/impy/aegis/IntroActivity.java +++ b/app/src/main/java/me/impy/aegis/IntroActivity.java @@ -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); diff --git a/app/src/main/java/me/impy/aegis/crypto/MasterKey.java b/app/src/main/java/me/impy/aegis/crypto/MasterKey.java index b4b8eb1f..cfbb1cbe 100644 --- a/app/src/main/java/me/impy/aegis/crypto/MasterKey.java +++ b/app/src/main/java/me/impy/aegis/crypto/MasterKey.java @@ -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(); + } } diff --git a/app/src/main/java/me/impy/aegis/crypto/slots/Slot.java b/app/src/main/java/me/impy/aegis/crypto/slots/Slot.java index 20a70605..fe887dfb 100644 --- a/app/src/main/java/me/impy/aegis/crypto/slots/Slot.java +++ b/app/src/main/java/me/impy/aegis/crypto/slots/Slot.java @@ -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); } diff --git a/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java b/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java index 00baa4e6..d0037b5f 100644 --- a/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java +++ b/app/src/main/java/me/impy/aegis/crypto/slots/SlotCollection.java @@ -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, Serializable { @@ -88,6 +94,16 @@ public class SlotCollection implements Iterable, Serializable { return null; } + public List findAll(Class type) { + ArrayList list = new ArrayList<>(); + for (Slot slot : this) { + if (slot.getClass() == type) { + list.add(type.cast(slot)); + } + } + return list; + } + public boolean has(Class type) { return find(type) != null; } @@ -97,11 +113,27 @@ public class SlotCollection implements Iterable, 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; } } diff --git a/app/src/main/java/me/impy/aegis/crypto/slots/SlotIntegrityException.java b/app/src/main/java/me/impy/aegis/crypto/slots/SlotIntegrityException.java new file mode 100644 index 00000000..d3090359 --- /dev/null +++ b/app/src/main/java/me/impy/aegis/crypto/slots/SlotIntegrityException.java @@ -0,0 +1,5 @@ +package me.impy.aegis.crypto.slots; + +public class SlotIntegrityException extends Exception { + +} diff --git a/app/src/main/java/me/impy/aegis/helpers/AuthHelper.java b/app/src/main/java/me/impy/aegis/helpers/AuthHelper.java index df7f17bd..f6c645de 100644 --- a/app/src/main/java/me/impy/aegis/helpers/AuthHelper.java +++ b/app/src/main/java/me/impy/aegis/helpers/AuthHelper.java @@ -34,5 +34,4 @@ public class AuthHelper { CryptoUtils.zero(passwordConfirm); return equal; } - }