diff --git a/app/src/main/java/me/impy/aegis/AuthActivity.java b/app/src/main/java/me/impy/aegis/AuthActivity.java index 2cf21f9a..1bde01ec 100644 --- a/app/src/main/java/me/impy/aegis/AuthActivity.java +++ b/app/src/main/java/me/impy/aegis/AuthActivity.java @@ -15,6 +15,7 @@ import android.widget.TextView; import com.mattprecious.swirl.SwirlView; import java.lang.reflect.UndeclaredThrowableException; +import java.security.InvalidKeyException; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -30,9 +31,6 @@ import me.impy.aegis.helpers.FingerprintUiHelper; import me.impy.aegis.helpers.AuthHelper; public class AuthActivity extends AegisActivity implements FingerprintUiHelper.Callback, SlotCollectionTask.Callback { - public static final int RESULT_OK = 0; - public static final int RESULT_EXCEPTION = 1; - private EditText _textPassword; private SlotCollection _slots; @@ -45,6 +43,7 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C setContentView(R.layout.activity_auth); _textPassword = findViewById(R.id.text_password); LinearLayout boxFingerprint = findViewById(R.id.box_fingerprint); + LinearLayout boxFingerprintInfo = findViewById(R.id.box_fingerprint_info); TextView textFingerprint = findViewById(R.id.text_fingerprint); SwirlView imgFingerprint = null; @@ -60,6 +59,7 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C // only show the fingerprint controls if the api version is new enough, permission is granted, a scanner is found and a fingerprint slot is found FingerprintManager manager = FingerprintHelper.getManager(this); if (manager != null && _slots.has(FingerprintSlot.class)) { + boolean invalidated = false; try { // find a fingerprint slot with an id that matches an alias in the keystore for (FingerprintSlot slot : _slots.findAll(FingerprintSlot.class)) { @@ -67,15 +67,26 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C KeyStoreHandle handle = new KeyStoreHandle(); if (handle.containsKey(id)) { SecretKey key = handle.getKey(id); + // if 'key' is null, it was permanently invalidated + if (key == null) { + invalidated = true; + continue; + } _fingerCipher = Slot.createCipher(key, Cipher.DECRYPT_MODE); _fingerHelper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this); boxFingerprint.setVisibility(View.VISIBLE); + invalidated = false; break; } } } catch (Exception e) { throw new UndeclaredThrowableException(e); } + + // display a help message if a matching invalidated keystore entry was found + if (invalidated) { + boxFingerprintInfo.setVisibility(View.VISIBLE); + } } Button button = findViewById(R.id.button_decrypt); diff --git a/app/src/main/java/me/impy/aegis/SlotHolder.java b/app/src/main/java/me/impy/aegis/SlotHolder.java index 46763565..55fe2cdf 100644 --- a/app/src/main/java/me/impy/aegis/SlotHolder.java +++ b/app/src/main/java/me/impy/aegis/SlotHolder.java @@ -6,6 +6,7 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; +import me.impy.aegis.crypto.KeyStoreHandle; import me.impy.aegis.crypto.slots.FingerprintSlot; import me.impy.aegis.crypto.slots.PasswordSlot; import me.impy.aegis.crypto.slots.RawSlot; @@ -29,13 +30,19 @@ public class SlotHolder extends RecyclerView.ViewHolder { public void setData(Slot slot) { if (slot instanceof PasswordSlot) { - _slotName.setText("Password 1"); + _slotName.setText("Password"); _slotImg.setImageResource(R.drawable.ic_create_black_24dp); } else if (slot instanceof FingerprintSlot) { - _slotName.setText("Finger 1"); + _slotName.setText("Finger"); _slotImg.setImageResource(R.drawable.ic_fingerprint_black_24dp); + try { + KeyStoreHandle keyStore = new KeyStoreHandle(); + if (keyStore.containsKey(slot.getID())) { + _slotUsed.setVisibility(View.VISIBLE); + } + } catch (Exception e) { } } else if (slot instanceof RawSlot) { - _slotName.setText("Raw 1"); + _slotName.setText("Raw"); _slotImg.setImageResource(R.drawable.ic_vpn_key_black_24dp); } else { throw new RuntimeException(); diff --git a/app/src/main/java/me/impy/aegis/crypto/KeyStoreHandle.java b/app/src/main/java/me/impy/aegis/crypto/KeyStoreHandle.java index 4488ca8c..7341ffc0 100644 --- a/app/src/main/java/me/impy/aegis/crypto/KeyStoreHandle.java +++ b/app/src/main/java/me/impy/aegis/crypto/KeyStoreHandle.java @@ -1,17 +1,22 @@ package me.impy.aegis.crypto; +import android.annotation.SuppressLint; import android.os.Build; import android.security.keystore.KeyGenParameterSpec; +import android.security.keystore.KeyPermanentlyInvalidatedException; import android.security.keystore.KeyProperties; import java.io.IOException; +import java.security.InvalidKeyException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.UnrecoverableKeyException; import java.security.cert.CertificateException; +import javax.crypto.Cipher; import javax.crypto.KeyGenerator; +import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import me.impy.aegis.crypto.slots.FingerprintSlot; @@ -47,7 +52,28 @@ public class KeyStoreHandle { } } - public SecretKey getKey(String id) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException { - return (SecretKey) _keyStore.getKey(id, null); + public SecretKey getKey(String id) + throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException { + SecretKey key = (SecretKey) _keyStore.getKey(id, null); + + // try to initialize a dummy cipher + // and see if KeyPermanentlyInvalidatedException is thrown + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + try { + @SuppressLint("GetInstance") + Cipher cipher = Cipher.getInstance(CryptoUtils.CRYPTO_CIPHER_RAW); + cipher.init(Cipher.ENCRYPT_MODE, key); + } catch (KeyPermanentlyInvalidatedException e) { + return null; + } catch (NoSuchPaddingException | InvalidKeyException e) { + throw new RuntimeException(e); + } + } + + return key; + } + + public void deleteKey(String id) throws KeyStoreException { + _keyStore.deleteEntry(id); } } diff --git a/app/src/main/res/layout/activity_auth.xml b/app/src/main/res/layout/activity_auth.xml index c2e0e3af..35ead88e 100644 --- a/app/src/main/res/layout/activity_auth.xml +++ b/app/src/main/res/layout/activity_auth.xml @@ -48,7 +48,7 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:layout_marginTop="12dp" - android:visibility="invisible"> + android:visibility="gone"> + + + + + +