2019-10-16 22:16:47 +02:00
|
|
|
package com.beemdevelopment.aegis.helpers;
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.biometric.BiometricPrompt;
|
|
|
|
import androidx.fragment.app.Fragment;
|
|
|
|
import androidx.fragment.app.FragmentActivity;
|
|
|
|
|
|
|
|
import com.beemdevelopment.aegis.crypto.KeyStoreHandle;
|
|
|
|
import com.beemdevelopment.aegis.crypto.KeyStoreHandleException;
|
2019-12-25 19:21:34 +01:00
|
|
|
import com.beemdevelopment.aegis.vault.slots.BiometricSlot;
|
|
|
|
import com.beemdevelopment.aegis.vault.slots.Slot;
|
|
|
|
import com.beemdevelopment.aegis.vault.slots.SlotException;
|
2019-10-16 22:16:47 +02:00
|
|
|
|
|
|
|
import java.util.Objects;
|
|
|
|
|
|
|
|
import javax.crypto.Cipher;
|
|
|
|
import javax.crypto.SecretKey;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A class that can prepare initialization of a BiometricSlot by generating a new
|
|
|
|
* key in the Android KeyStore and authenticating a cipher for it through a
|
|
|
|
* BiometricPrompt.
|
|
|
|
*/
|
|
|
|
public class BiometricSlotInitializer extends BiometricPrompt.AuthenticationCallback {
|
|
|
|
private BiometricSlot _slot;
|
|
|
|
private Listener _listener;
|
|
|
|
private BiometricPrompt _prompt;
|
|
|
|
|
|
|
|
public BiometricSlotInitializer(Fragment fragment, Listener listener) {
|
|
|
|
_listener = listener;
|
|
|
|
_prompt = new BiometricPrompt(fragment, new UiThreadExecutor(), this);
|
|
|
|
}
|
|
|
|
|
|
|
|
public BiometricSlotInitializer(FragmentActivity activity, Listener listener) {
|
|
|
|
_listener = listener;
|
|
|
|
_prompt = new BiometricPrompt(activity, new UiThreadExecutor(), this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generates a new key in the Android KeyStore for the new BiometricSlot,
|
|
|
|
* initializes a cipher with it and shows a BiometricPrompt to the user for
|
|
|
|
* authentication. If authentication is successful, the new slot will be
|
|
|
|
* initialized and delivered back through the listener.
|
|
|
|
*/
|
|
|
|
public void authenticate(BiometricPrompt.PromptInfo info) {
|
|
|
|
if (_slot != null) {
|
|
|
|
throw new IllegalStateException("Biometric authentication already in progress");
|
|
|
|
}
|
|
|
|
|
|
|
|
KeyStoreHandle keyStore;
|
|
|
|
try {
|
|
|
|
keyStore = new KeyStoreHandle();
|
|
|
|
} catch (KeyStoreHandleException e) {
|
|
|
|
fail(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate a new Android KeyStore key
|
|
|
|
// and assign it the UUID of the new slot as an alias
|
|
|
|
Cipher cipher;
|
|
|
|
BiometricSlot slot = new BiometricSlot();
|
|
|
|
try {
|
|
|
|
SecretKey key = keyStore.generateKey(slot.getUUID().toString());
|
|
|
|
cipher = Slot.createEncryptCipher(key);
|
|
|
|
} catch (KeyStoreHandleException | SlotException e) {
|
|
|
|
fail(e);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
_slot = slot;
|
|
|
|
_prompt.authenticate(info, new BiometricPrompt.CryptoObject(cipher));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Cancels the BiometricPrompt and resets the state of the initializer. It will
|
|
|
|
* also attempt to delete the previously generated Android KeyStore key.
|
|
|
|
*/
|
|
|
|
public void cancelAuthentication() {
|
|
|
|
if (_slot == null) {
|
|
|
|
throw new IllegalStateException("Biometric authentication not in progress");
|
|
|
|
}
|
|
|
|
|
|
|
|
reset();
|
|
|
|
_prompt.cancelAuthentication();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void reset() {
|
|
|
|
if (_slot != null) {
|
|
|
|
try {
|
|
|
|
// clean up the unused KeyStore key
|
|
|
|
// this is non-critical, so just fail silently if an error occurs
|
|
|
|
String uuid = _slot.getUUID().toString();
|
|
|
|
KeyStoreHandle keyStore = new KeyStoreHandle();
|
|
|
|
if (keyStore.containsKey(uuid)) {
|
|
|
|
keyStore.deleteKey(uuid);
|
|
|
|
}
|
|
|
|
} catch (KeyStoreHandleException e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
}
|
|
|
|
|
|
|
|
_slot = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void fail(int errorCode, CharSequence errString) {
|
|
|
|
reset();
|
|
|
|
_listener.onSlotInitializationFailed(errorCode, errString);
|
|
|
|
}
|
|
|
|
|
|
|
|
private void fail(Exception e) {
|
|
|
|
e.printStackTrace();
|
|
|
|
fail(0, e.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
|
|
|
|
super.onAuthenticationError(errorCode, errString);
|
|
|
|
fail(errorCode, errString.toString());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
|
|
|
|
super.onAuthenticationSucceeded(result);
|
|
|
|
_listener.onInitializeSlot(_slot, Objects.requireNonNull(result.getCryptoObject()).getCipher());
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onAuthenticationFailed() {
|
|
|
|
super.onAuthenticationFailed();
|
|
|
|
}
|
|
|
|
|
|
|
|
public interface Listener {
|
|
|
|
void onInitializeSlot(BiometricSlot slot, Cipher cipher);
|
|
|
|
void onSlotInitializationFailed(int errorCode, @NonNull CharSequence errString);
|
|
|
|
}
|
|
|
|
}
|