allow to disable fingerprint unlock (in QuickUnlock mode) after three failed attempts, closes #16

This commit is contained in:
Philipp Crocoll
2020-02-16 21:26:33 +01:00
parent ad0ac93bd3
commit 01ea54932c
9 changed files with 126 additions and 67 deletions

View File

@@ -119,6 +119,7 @@ namespace keepass2android
{
void OnBiometricAuthSucceeded();
void OnBiometricError(string toString);
void OnBiometricAttemptFailed(string message);
}
public class BiometricModule
@@ -234,6 +235,7 @@ namespace keepass2android
private BiometricPrompt _biometricPrompt;
private FragmentActivity _activity;
private BiometricAuthCallbackAdapter _biometricAuthCallbackAdapter;
public BiometricCrypt(BiometricModule biometric, string keyId)
{
@@ -261,13 +263,14 @@ namespace keepass2android
public void StartListening(IBiometricAuthCallback callback)
{
StartListening(new BiometricAuthCallbackAdapter(callback, _activity));
_biometricAuthCallbackAdapter = new BiometricAuthCallbackAdapter(callback, _activity);
StartListening(_biometricAuthCallbackAdapter);
}
public void StopListening()
{
_biometricAuthCallbackAdapter.IgnoreNextError();
_biometricPrompt.CancelAuthentication();
}
public bool HasUserInterface
@@ -520,6 +523,7 @@ namespace keepass2android
{
private readonly IBiometricAuthCallback _callback;
private readonly Context _context;
private bool _ignoreNextError;
public BiometricAuthCallbackAdapter(IBiometricAuthCallback callback, Context context)
{
@@ -535,16 +539,24 @@ namespace keepass2android
public override void OnAuthenticationError(int errorCode, ICharSequence errString)
{
if (_ignoreNextError)
{
_ignoreNextError = false;
return;
}
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricError(errString.ToString()));
}
public override void OnAuthenticationFailed()
{
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricError(_context.Resources.GetString(Resource.String.fingerprint_not_recognized)));
new Handler(Looper.MainLooper).Post(() => _callback.OnBiometricAttemptFailed(_context.Resources.GetString(Resource.String.fingerprint_not_recognized)));
}
public void IgnoreNextError()
{
_ignoreNextError = true;
}
}
}

View File

@@ -120,26 +120,27 @@ namespace keepass2android
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
FindViewById<CheckBox>(Resource.Id.show_keyboard_while_fingerprint).Checked =
Util.GetShowKeyboardDuringFingerprintUnlock(this);
FindViewById<CheckBox>(Resource.Id.show_keyboard_while_fingerprint).CheckedChange += (sender, args) =>
{
PreferenceManager.GetDefaultSharedPreferences(this)
.Edit()
.PutBoolean(GetString(Resource.String.ShowKeyboardWhileFingerprint_key), args.IsChecked)
.Commit();
};
UpdateKeyboardCheckboxVisibility();
}
FindViewById<CheckBox>(Resource.Id.close_database_after_failed).Checked =
Util.GetCloseDatabaseAfterFailedBiometricQuickUnlock(this);
private void UpdateKeyboardCheckboxVisibility()
FindViewById<CheckBox>(Resource.Id.close_database_after_failed).CheckedChange += (sender, args) =>
{
PreferenceManager.GetDefaultSharedPreferences(this)
.Edit()
.PutBoolean(GetString(Resource.String.CloseDatabaseAfterFailedBiometricQuickUnlock_key), args.IsChecked)
.Commit();
};
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
}
private void UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility()
{
FindViewById(Resource.Id.show_keyboard_while_fingerprint).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.close_database_after_failed).Visibility = _unlockMode == FingerprintUnlockMode.QuickUnlock ? ViewStates.Visible : ViewStates.Gone;
}
private bool TrySetupSamsung()
@@ -249,7 +250,7 @@ namespace keepass2android
if (_samsungBiometry != null)
{
_unlockMode = newMode;
UpdateKeyboardCheckboxVisibility();
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
ISharedPreferencesEditor edit = PreferenceManager.GetDefaultSharedPreferences(this).Edit();
edit.PutString(App.Kp2a.CurrentDb.CurrentFingerprintModePrefKey, _unlockMode.ToString());
@@ -260,17 +261,17 @@ namespace keepass2android
if (newMode == FingerprintUnlockMode.Disabled)
{
_unlockMode = newMode;
UpdateKeyboardCheckboxVisibility();
StoreUnlockMode();
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
StoreUnlockMode();
return;
}
_desiredUnlockMode = newMode;
FindViewById(Resource.Id.radio_buttons).Visibility = ViewStates.Gone;
FindViewById(Resource.Id.show_keyboard_while_fingerprint).Visibility = ViewStates.Gone;
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Visible;
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Visible;
try
{
_enc = new BiometricEncryption(new BiometricModule(this), CurrentPreferenceKey);
@@ -312,7 +313,7 @@ namespace keepass2android
FindViewById(Resource.Id.fingerprint_auth_container).Visibility = ViewStates.Gone;
StoreUnlockMode();
UpdateKeyboardCheckboxVisibility();
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
}, SUCCESS_DELAY_MILLIS);
@@ -332,7 +333,12 @@ namespace keepass2android
_fpTextView.PostDelayed(ResetErrorTextRunnable, ERROR_TIMEOUT_MILLIS);
}
void ResetErrorTextRunnable()
public void OnBiometricAttemptFailed(string message)
{
//ignore
}
void ResetErrorTextRunnable()
{
_fpTextView.SetTextColor(
_fpTextView.Resources.GetColor(Resource.Color.hint_color, null));
@@ -351,7 +357,7 @@ namespace keepass2android
//seems like not all Samsung Devices (e.g. Note 4) don't support the Android 6 fingerprint API
if (!TrySetupSamsung())
SetError(Resource.String.fingerprint_hardware_error);
UpdateKeyboardCheckboxVisibility();
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
return;
}
if (!fpModule.IsAvailable)
@@ -360,8 +366,8 @@ namespace keepass2android
return;
}
ShowRadioButtons();
UpdateKeyboardCheckboxVisibility();
UpdateCloseDatabaseAfterFailedBiometricQuickUnlockVisibility();
}
protected override void OnPause()

View File

@@ -928,7 +928,12 @@ namespace keepass2android
Toast.MakeText(this, message, ToastLength.Long).Show();
}
public void OnBiometricAuthSucceeded()
public void OnBiometricAttemptFailed(string message)
{
//ignore
}
public void OnBiometricAuthSucceeded()
{
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
@@ -1761,17 +1766,9 @@ namespace keepass2android
}
else
{
bool showKeyboard = true;
bool showKeyboard = (Util.GetShowKeyboardDuringFingerprintUnlock(this));
if (!fingerprintInitialized)
showKeyboard = true;
EditText pwd = (EditText) FindViewById(Resource.Id.password_edit);
pwd.PostDelayed(() =>
{

View File

@@ -48,7 +48,10 @@ namespace keepass2android
private int _quickUnlockLength;
private const int FingerprintPermissionRequestCode = 0;
public QuickUnlock()
private int numFailedAttempts = 0;
private int maxNumFailedAttempts = int.MaxValue;
public QuickUnlock()
{
_design = new ActivityDesign(this);
}
@@ -63,7 +66,9 @@ namespace keepass2android
_ioc = App.Kp2a.GetDbForQuickUnlock()?.Ioc;
if (_ioc == null)
if (_ioc == null)
{
Finish();
return;
@@ -147,11 +152,23 @@ namespace keepass2android
Util.SetNoPersonalizedLearning(FindViewById<EditText>(Resource.Id.QuickUnlock_password));
if (bundle != null)
numFailedAttempts = bundle.GetInt(NumFailedAttemptsKey, 0);
}
protected override void OnStart()
private const string NumFailedAttemptsKey = "FailedAttempts";
protected override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
outState.PutInt(NumFailedAttemptsKey, numFailedAttempts);
}
protected override void OnStart()
{
base.OnStart();
DonateReminder.ShowDonateReminderIfAppropriate(this);
@@ -174,7 +191,18 @@ namespace keepass2android
Toast.MakeText(this, message, ToastLength.Long).Show();
}
public void OnBiometricAuthSucceeded()
public void OnBiometricAttemptFailed(string message)
{
numFailedAttempts++;
if (numFailedAttempts >= maxNumFailedAttempts)
{
FindViewById<ImageButton>(Resource.Id.fingerprintbtn).Visibility = ViewStates.Gone;
_biometryIdentifier.StopListening();
}
}
public void OnBiometricAuthSucceeded()
{
Kp2aLog.Log("OnFingerprintAuthSucceeded");
_biometryIdentifier.StopListening();
@@ -218,7 +246,14 @@ namespace keepass2android
return false;
}
BiometricModule fpModule = new BiometricModule(this);
if (um == FingerprintUnlockMode.QuickUnlock && Util.GetCloseDatabaseAfterFailedBiometricQuickUnlock(this))
{
maxNumFailedAttempts = 3;
}
BiometricModule fpModule = new BiometricModule(this);
Kp2aLog.Log("fpModule.IsHardwareAvailable=" + fpModule.IsHardwareAvailable);
if (fpModule.IsHardwareAvailable) //see FingerprintSetupActivity
_biometryIdentifier = new BiometricDecryption(fpModule, App.Kp2a.GetDbForQuickUnlock().CurrentFingerprintPrefKey, this,
@@ -232,9 +267,15 @@ namespace keepass2android
_biometryIdentifier = new BiometrySamsungIdentifier(this);
btn.Click += (sender, args) =>
{
if (_biometryIdentifier.Init())
_biometryIdentifier.StartListening(this);
};
if (_biometryIdentifier.Init())
{
if (numFailedAttempts < maxNumFailedAttempts)
{
_biometryIdentifier.StartListening(this);
}
}
};
Kp2aLog.Log("trying Samsung Fingerprint API...Seems to work!");
}
catch (Exception)
@@ -330,8 +371,9 @@ namespace keepass2android
CheckIfUnloaded();
InitFingerprintUnlock();
bool showKeyboard = ((!InitFingerprintUnlock()) || (Util.GetShowKeyboardDuringFingerprintUnlock(this)));
bool showKeyboard = true;
EditText pwd = (EditText)FindViewById(Resource.Id.QuickUnlock_password);
pwd.PostDelayed(() =>
@@ -347,7 +389,7 @@ namespace keepass2android
var btn = FindViewById<ImageButton>(Resource.Id.fingerprintbtn);
btn.Click += (sender, args) =>
{
if (_biometryIdentifier.HasUserInterface|| string.IsNullOrEmpty((string)btn.Tag) )
if ((_biometryIdentifier != null) && ((_biometryIdentifier.HasUserInterface)|| string.IsNullOrEmpty((string)btn.Tag) ))
{
_biometryIdentifier.StartListening(this);
}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
@@ -68,11 +68,11 @@
android:background="?android:attr/selectableItemBackground" />
</LinearLayout>
<CheckBox
android:id="@+id/show_keyboard_while_fingerprint"
android:id="@+id/close_database_after_failed"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:text="@string/ShowKeyboardDuringFingerprintAuth" />
android:text="@string/CloseDbAfterFailedAttempts" />
</LinearLayout>
<RelativeLayout
android:id="@+id/fingerprint_auth_container"

View File

@@ -82,7 +82,7 @@
<bool name="ShowUsernameInList_default">true</bool>
<bool name="ShowGroupnameInSearchResult_default">true</bool>
<string name="ViewDatabaseSecure_key">ViewDatabaseSecure</string>
<string name="ShowKeyboardWhileFingerprint_key">ShowKeyboardWhileFingerprint_key</string>
<string name="CloseDatabaseAfterFailedBiometricQuickUnlock_key">CloseDatabaseAfterFailedBiometricQuickUnlock_key</string>
<bool name="RememberRecentFiles_default">true</bool>
<string name="TrayTotp_SettingsField_key">TrayTotp_SettingsField_key</string>
<string name="TrayTotp_SeedField_key">TrayTotp_SeedField_key</string>

View File

@@ -779,7 +779,7 @@
<string name="EntryChannel_name">Entry notifications</string>
<string name="EntryChannel_desc">Notification to simplify access to the currently selected entry.</string>
<string name="ShowKeyboardDuringFingerprintAuth">Show soft keyboard for password input when biometric authentication is active.</string>
<string name="CloseDbAfterFailedAttempts">Close database after three failed biometric unlock attempts.</string>
<string-array name="ChangeLog_1_08">
<item>Add notification button for copying TOTP to clipboard</item>

View File

@@ -541,15 +541,19 @@ namespace keepass2android
}
}
public static bool GetShowKeyboardDuringFingerprintUnlock(Context ctx)
{
return (PreferenceManager.GetDefaultSharedPreferences(ctx).GetBoolean(
ctx.GetString(Resource.String.ShowKeyboardWhileFingerprint_key), true));
}
public static bool GetCloseDatabaseAfterFailedBiometricQuickUnlock(Context ctx)
{
return (PreferenceManager.GetDefaultSharedPreferences(ctx).GetBoolean(
ctx.GetString(Resource.String.CloseDatabaseAfterFailedBiometricQuickUnlock_key), true));
}
public static void MoveBottomBarButtons(int btn1Id, int btn2Id, int bottomBarId, Activity context)
public static void MoveBottomBarButtons(int btn1Id, int btn2Id, int bottomBarId, Activity context)
{
var btn1 = context.FindViewById<Button>(btn1Id);
var btn2 = context.FindViewById<Button>(btn2Id);

View File

@@ -44,8 +44,6 @@ namespace keepass2android.view
get { return base.Activated; }
set
{
Log.Debug("KP2A", "Setting activated = " + value);
if (value)
{
FindViewById(Resource.Id.icon).Visibility = ViewStates.Invisible;