Display a message if all fingerprint slots have been invalidated

This commit is contained in:
Alexander Bakker 2018-02-13 13:25:13 +01:00
parent 576f908e01
commit a52c5b61c0
4 changed files with 72 additions and 9 deletions

View file

@ -15,6 +15,7 @@ import android.widget.TextView;
import com.mattprecious.swirl.SwirlView; import com.mattprecious.swirl.SwirlView;
import java.lang.reflect.UndeclaredThrowableException; import java.lang.reflect.UndeclaredThrowableException;
import java.security.InvalidKeyException;
import javax.crypto.Cipher; import javax.crypto.Cipher;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
@ -30,9 +31,6 @@ import me.impy.aegis.helpers.FingerprintUiHelper;
import me.impy.aegis.helpers.AuthHelper; import me.impy.aegis.helpers.AuthHelper;
public class AuthActivity extends AegisActivity implements FingerprintUiHelper.Callback, SlotCollectionTask.Callback { 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 EditText _textPassword;
private SlotCollection _slots; private SlotCollection _slots;
@ -45,6 +43,7 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C
setContentView(R.layout.activity_auth); setContentView(R.layout.activity_auth);
_textPassword = findViewById(R.id.text_password); _textPassword = findViewById(R.id.text_password);
LinearLayout boxFingerprint = findViewById(R.id.box_fingerprint); LinearLayout boxFingerprint = findViewById(R.id.box_fingerprint);
LinearLayout boxFingerprintInfo = findViewById(R.id.box_fingerprint_info);
TextView textFingerprint = findViewById(R.id.text_fingerprint); TextView textFingerprint = findViewById(R.id.text_fingerprint);
SwirlView imgFingerprint = null; 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 // 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); FingerprintManager manager = FingerprintHelper.getManager(this);
if (manager != null && _slots.has(FingerprintSlot.class)) { if (manager != null && _slots.has(FingerprintSlot.class)) {
boolean invalidated = false;
try { try {
// find a fingerprint slot with an id that matches an alias in the keystore // find a fingerprint slot with an id that matches an alias in the keystore
for (FingerprintSlot slot : _slots.findAll(FingerprintSlot.class)) { for (FingerprintSlot slot : _slots.findAll(FingerprintSlot.class)) {
@ -67,15 +67,26 @@ public class AuthActivity extends AegisActivity implements FingerprintUiHelper.C
KeyStoreHandle handle = new KeyStoreHandle(); KeyStoreHandle handle = new KeyStoreHandle();
if (handle.containsKey(id)) { if (handle.containsKey(id)) {
SecretKey key = handle.getKey(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); _fingerCipher = Slot.createCipher(key, Cipher.DECRYPT_MODE);
_fingerHelper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this); _fingerHelper = new FingerprintUiHelper(manager, imgFingerprint, textFingerprint, this);
boxFingerprint.setVisibility(View.VISIBLE); boxFingerprint.setVisibility(View.VISIBLE);
invalidated = false;
break; break;
} }
} }
} catch (Exception e) { } catch (Exception e) {
throw new UndeclaredThrowableException(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); Button button = findViewById(R.id.button_decrypt);

View file

@ -6,6 +6,7 @@ import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import me.impy.aegis.crypto.KeyStoreHandle;
import me.impy.aegis.crypto.slots.FingerprintSlot; 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.RawSlot; import me.impy.aegis.crypto.slots.RawSlot;
@ -29,13 +30,19 @@ public class SlotHolder extends RecyclerView.ViewHolder {
public void setData(Slot slot) { public void setData(Slot slot) {
if (slot instanceof PasswordSlot) { if (slot instanceof PasswordSlot) {
_slotName.setText("Password 1"); _slotName.setText("Password");
_slotImg.setImageResource(R.drawable.ic_create_black_24dp); _slotImg.setImageResource(R.drawable.ic_create_black_24dp);
} else if (slot instanceof FingerprintSlot) { } else if (slot instanceof FingerprintSlot) {
_slotName.setText("Finger 1"); _slotName.setText("Finger");
_slotImg.setImageResource(R.drawable.ic_fingerprint_black_24dp); _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) { } else if (slot instanceof RawSlot) {
_slotName.setText("Raw 1"); _slotName.setText("Raw");
_slotImg.setImageResource(R.drawable.ic_vpn_key_black_24dp); _slotImg.setImageResource(R.drawable.ic_vpn_key_black_24dp);
} else { } else {
throw new RuntimeException(); throw new RuntimeException();

View file

@ -1,17 +1,22 @@
package me.impy.aegis.crypto; package me.impy.aegis.crypto;
import android.annotation.SuppressLint;
import android.os.Build; import android.os.Build;
import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyPermanentlyInvalidatedException;
import android.security.keystore.KeyProperties; import android.security.keystore.KeyProperties;
import java.io.IOException; import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator; import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import me.impy.aegis.crypto.slots.FingerprintSlot; import me.impy.aegis.crypto.slots.FingerprintSlot;
@ -47,7 +52,28 @@ public class KeyStoreHandle {
} }
} }
public SecretKey getKey(String id) throws UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException { public SecretKey getKey(String id)
return (SecretKey) _keyStore.getKey(id, null); 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);
} }
} }

View file

@ -48,7 +48,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_marginTop="12dp" android:layout_marginTop="12dp"
android:visibility="invisible"> android:visibility="gone">
<LinearLayout <LinearLayout
android:id="@+id/img_fingerprint_insert" android:id="@+id/img_fingerprint_insert"
@ -65,5 +65,24 @@
android:text="@string/fingerprint_hint" android:text="@string/fingerprint_hint"
android:textColor="?attr/secondaryText"/> android:textColor="?attr/secondaryText"/>
</LinearLayout> </LinearLayout>
<LinearLayout
android:id="@+id/box_fingerprint_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="12dp"
android:visibility="gone">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_info_outline_black_24dp"
android:layout_marginEnd="15dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="A fingerprint slot was found, but it appears to have been invalidated by the Android keystore. Go to &quot;Settings -> Key slots&quot; to readd your fingerprint."/>
</LinearLayout>
</LinearLayout> </LinearLayout>
</android.support.design.widget.CoordinatorLayout> </android.support.design.widget.CoordinatorLayout>