diff --git a/app/src/main/java/com/keepassdroid/fingerprint/FingerPrintHelper.java b/app/src/main/java/com/keepassdroid/fingerprint/FingerPrintHelper.java index 749b9c304..933699411 100644 --- a/app/src/main/java/com/keepassdroid/fingerprint/FingerPrintHelper.java +++ b/app/src/main/java/com/keepassdroid/fingerprint/FingerPrintHelper.java @@ -30,6 +30,7 @@ import android.support.annotation.RequiresApi; import android.support.v4.hardware.fingerprint.FingerprintManagerCompat; import android.support.v4.os.CancellationSignal; import android.util.Base64; +import android.util.Log; import java.io.IOException; import java.security.KeyStore; @@ -47,7 +48,9 @@ import javax.crypto.spec.IvParameterSpec; @RequiresApi(api = Build.VERSION_CODES.M) public class FingerPrintHelper { - private static final String FINGERPRINT_KEYSTORE_KEY = "example-key"; + private static final String TAG = FingerPrintHelper.class.getName(); + + private static final String FINGERPRINT_KEYSTORE_KEY = "com.kunzisoft.keepass.fingerprint.key"; private FingerprintManagerCompat fingerprintManager; private KeyStore keyStore = null; @@ -65,8 +68,7 @@ public class FingerPrintHelper { this.authenticationCallback = authenticationCallback; } - public void startListening() { - + public synchronized void startListening() { // starts listening for fingerprints with the initialised crypto object cancellationSignal = new CancellationSignal(); fingerprintManager.authenticate( @@ -77,7 +79,7 @@ public class FingerPrintHelper { null); } - public void stopListening() { + public synchronized void stopListening() { if (!isFingerprintInitialized(false)) { return; } @@ -113,6 +115,7 @@ public class FingerPrintHelper { this.cryptoObject = new FingerprintManagerCompat.CryptoObject(cipher); setInitOk(true); } catch (final Exception e) { + Log.e(TAG, "Unable to initialize the keystore", e); setInitOk(false); fingerPrintCallback.onFingerPrintException(e); } @@ -142,6 +145,8 @@ public class FingerPrintHelper { return; } try { + stopListening(); + createNewKeyIfNeeded(false); // no need to keep deleting existing keys keyStore.load(null); final SecretKey key = (SecretKey) keyStore.getKey(FINGERPRINT_KEYSTORE_KEY, null); @@ -149,10 +154,13 @@ public class FingerPrintHelper { startListening(); } catch (final UnrecoverableKeyException unrecoverableKeyException) { + Log.e(TAG, "Unable to initialize encrypt data", unrecoverableKeyException); deleteEntryKey(); } catch (final KeyPermanentlyInvalidatedException invalidKeyException) { + Log.e(TAG, "Unable to initialize encrypt data", invalidKeyException); fingerPrintCallback.onInvalidKeyException(invalidKeyException); } catch (final Exception e) { + Log.e(TAG, "Unable to initialize encrypt data", e); fingerPrintCallback.onFingerPrintException(e); } } @@ -164,14 +172,15 @@ public class FingerPrintHelper { try { // actual do encryption here byte[] encrypted = cipher.doFinal(value.getBytes()); - final String encryptedValue = Base64.encodeToString(encrypted, Base64.DEFAULT); + final String encryptedValue = Base64.encodeToString(encrypted, Base64.NO_WRAP); // passes updated iv spec on to callback so this can be stored for decryption final IvParameterSpec spec = cipher.getParameters().getParameterSpec(IvParameterSpec.class); - final String ivSpecValue = Base64.encodeToString(spec.getIV(), Base64.DEFAULT); + final String ivSpecValue = Base64.encodeToString(spec.getIV(), Base64.NO_WRAP); fingerPrintCallback.handleEncryptedResult(encryptedValue, ivSpecValue); } catch (final Exception e) { + Log.e(TAG, "Unable to encrypt data", e); fingerPrintCallback.onFingerPrintException(e); } } @@ -181,21 +190,26 @@ public class FingerPrintHelper { return; } try { + stopListening(); + createNewKeyIfNeeded(false); keyStore.load(null); final SecretKey key = (SecretKey) keyStore.getKey(FINGERPRINT_KEYSTORE_KEY, null); // important to restore spec here that was used for decryption - final byte[] iv = Base64.decode(ivSpecValue, Base64.DEFAULT); + final byte[] iv = Base64.decode(ivSpecValue, Base64.NO_WRAP); final IvParameterSpec spec = new IvParameterSpec(iv); cipher.init(Cipher.DECRYPT_MODE, key, spec); startListening(); - } catch (final KeyPermanentlyInvalidatedException invalidKeyException) { - fingerPrintCallback.onInvalidKeyException(invalidKeyException); } catch (final UnrecoverableKeyException unrecoverableKeyException) { + Log.e(TAG, "Unable to initialize decrypt data", unrecoverableKeyException); deleteEntryKey(); + } catch (final KeyPermanentlyInvalidatedException invalidKeyException) { + Log.e(TAG, "Unable to initialize decrypt data", invalidKeyException); + fingerPrintCallback.onInvalidKeyException(invalidKeyException); } catch (final Exception e) { + Log.e(TAG, "Unable to initialize decrypt data", e); fingerPrintCallback.onFingerPrintException(e); } } @@ -206,15 +220,17 @@ public class FingerPrintHelper { } try { // actual decryption here - final byte[] encrypted = Base64.decode(encryptedValue, Base64.DEFAULT); + final byte[] encrypted = Base64.decode(encryptedValue, Base64.NO_WRAP); byte[] decrypted = cipher.doFinal(encrypted); final String decryptedString = new String(decrypted); //final String encryptedString = Base64.encodeToString(encrypted, 0 /* flags */); fingerPrintCallback.handleDecryptedResult(decryptedString); } catch (final BadPaddingException badPaddingException) { + Log.e(TAG, "Unable to decrypt data", badPaddingException); fingerPrintCallback.onInvalidKeyException(badPaddingException); } catch (final Exception e) { + Log.e(TAG, "Unable to decrypt data", e); fingerPrintCallback.onFingerPrintException(e); } } @@ -249,6 +265,7 @@ public class FingerPrintHelper { keyGenerator.generateKey(); } } catch (final Exception e) { + Log.e(TAG, "Unable to create a key in keystore", e); fingerPrintCallback.onFingerPrintException(e); } } @@ -262,6 +279,7 @@ public class FingerPrintHelper { | NoSuchAlgorithmException | IOException | NullPointerException e) { + Log.e(TAG, "Unable to delete entry key in keystore", e); if (fingerPrintCallback != null) fingerPrintCallback.onFingerPrintException(e); } diff --git a/app/src/main/java/com/keepassdroid/password/PasswordActivity.java b/app/src/main/java/com/keepassdroid/password/PasswordActivity.java index 265434f76..038ad9cce 100644 --- a/app/src/main/java/com/keepassdroid/password/PasswordActivity.java +++ b/app/src/main/java/com/keepassdroid/password/PasswordActivity.java @@ -89,6 +89,8 @@ import static com.keepassdroid.fingerprint.FingerPrintHelper.Mode.STORE_MODE; public class PasswordActivity extends StylishActivity implements FingerPrintHelper.FingerPrintCallback, UriIntentInitTaskCallback { + private static final String TAG = PasswordActivity.class.getName(); + public static final String KEY_DEFAULT_FILENAME = "defaultFileName"; private static final String KEY_PASSWORD = "password"; @@ -440,7 +442,7 @@ public class PasswordActivity extends StylishActivity if ( !fingerprintMustBeConfigured ) { final boolean validInput = s.length() > 0; // encrypt or decrypt mode based on how much input or not - setFingerPrintTextView(validInput ? R.string.store_with_fingerprint : R.string.scanning_fingerprint); + setFingerPrintView(validInput ? R.string.store_with_fingerprint : R.string.scanning_fingerprint); if (validInput) toggleFingerprintMode(STORE_MODE); else @@ -455,22 +457,30 @@ public class PasswordActivity extends StylishActivity public void onAuthenticationError( final int errorCode, final CharSequence errString) { - Log.i(getClass().getName(), errString.toString()); + switch (errorCode) { + case 5: + Log.i(TAG, "Fingerprint authentication error. Code : " + errorCode + " Error : " + errString); + break; + default: + Log.e(TAG, "Fingerprint authentication error. Code : " + errorCode + " Error : " + errString); + setFingerPrintView(errString.toString(), true); + } } @Override public void onAuthenticationHelp( final int helpCode, final CharSequence helpString) { + Log.w(TAG, "Fingerprint authentication help. Code : " + helpCode + " Help : " + helpString); showError(helpString); - reInitWithSameFingerprintMode(); + setFingerPrintView(helpString.toString(), true); fingerprintTextView.setText(helpString); } @Override public void onAuthenticationFailed() { + Log.e(TAG, "Fingerprint authentication failed, fingerprint not recognized"); showError(R.string.fingerprint_not_recognized); - reInitWithSameFingerprintMode(); } @Override @@ -504,7 +514,7 @@ public class PasswordActivity extends StylishActivity @RequiresApi(api = Build.VERSION_CODES.M) private void initEncryptData() { - setFingerPrintTextView(R.string.store_with_fingerprint); + setFingerPrintView(R.string.store_with_fingerprint); fingerPrintMode = STORE_MODE; if (fingerPrintHelper != null) fingerPrintHelper.initEncryptData(); @@ -512,7 +522,7 @@ public class PasswordActivity extends StylishActivity @RequiresApi(api = Build.VERSION_CODES.M) private void initDecryptData() { - setFingerPrintTextView(R.string.scanning_fingerprint); + setFingerPrintView(R.string.scanning_fingerprint); fingerPrintMode = OPEN_MODE; if (fingerPrintHelper != null) { final String ivSpecValue = prefsNoBackup.getString(getPreferenceKeyIvSpec(), null); @@ -525,12 +535,12 @@ public class PasswordActivity extends StylishActivity private synchronized void toggleFingerprintMode(final FingerPrintHelper.Mode newMode) { if( !newMode.equals(fingerPrintMode) ) { fingerPrintMode = newMode; - reInitWithSameFingerprintMode(); + reInitWithFingerprintMode(); } } @RequiresApi(api = Build.VERSION_CODES.M) - private synchronized void reInitWithSameFingerprintMode() { + private synchronized void reInitWithFingerprintMode() { switch (fingerPrintMode) { case STORE_MODE: initEncryptData(); @@ -545,8 +555,6 @@ public class PasswordActivity extends StylishActivity @Override protected void onPause() { - super.onPause(); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (fingerPrintAnimatedVector != null) { fingerPrintAnimatedVector.stopScan(); @@ -557,18 +565,33 @@ public class PasswordActivity extends StylishActivity fingerPrintHelper.stopListening(); } } + super.onPause(); } private void setFingerPrintVisibility(final int vis) { runOnUiThread(() -> fingerprintContainerView.setVisibility(vis)); } - private void setFingerPrintTextView(final int textId) { - runOnUiThread(() -> fingerprintTextView.setText(textId)); + private void setFingerPrintView(final int textId) { + setFingerPrintView(textId, false); } - private void setFingerPrintAlphaImageView(final float alpha) { - runOnUiThread(() -> fingerprintContainerView.setAlpha(alpha)); + private void setFingerPrintView(final CharSequence text) { + setFingerPrintView(text, false); + } + + private void setFingerPrintView(final int textId, boolean lock) { + setFingerPrintView(getString(textId), lock); + } + + private void setFingerPrintView(final CharSequence text, boolean lock) { + runOnUiThread(() -> { + if (lock) { + fingerprintContainerView.setAlpha(0.6f); + } else + fingerprintContainerView.setAlpha(1f); + fingerprintTextView.setText(text); + }); } @RequiresApi(api = Build.VERSION_CODES.M) @@ -589,18 +612,16 @@ public class PasswordActivity extends StylishActivity setFingerPrintVisibility(View.VISIBLE); if (!fingerPrintHelper.hasEnrolledFingerprints()) { - setFingerPrintAlphaImageView(0.6f); // This happens when no fingerprints are registered. Listening won't start - setFingerPrintTextView(R.string.configure_fingerprint); + setFingerPrintView(R.string.configure_fingerprint, true); } // finally fingerprint available and configured so we can use it else { fingerprintMustBeConfigured = false; - setFingerPrintAlphaImageView(1f); // fingerprint available but no stored password found yet for this DB so show info don't listen if (!prefsNoBackup.contains(getPreferenceKeyValue())) { - setFingerPrintTextView(R.string.no_password_stored); + setFingerPrintView(R.string.no_password_stored); // listen for encryption initEncryptData(); } @@ -616,7 +637,7 @@ public class PasswordActivity extends StylishActivity invalidateOptionsMenu(); } - private void removePrefsNoBackupKeys() { + private void removePrefsNoBackupKey() { prefsNoBackup.edit() .remove(getPreferenceKeyValue()) .remove(getPreferenceKeyIvSpec()) @@ -632,7 +653,7 @@ public class PasswordActivity extends StylishActivity .putString(getPreferenceKeyIvSpec(), ivSpec) .apply(); verifyAllViewsAndLoadDatabase(); - setFingerPrintTextView(R.string.encrypted_value_stored); + setFingerPrintView(R.string.encrypted_value_stored); } @Override @@ -644,22 +665,27 @@ public class PasswordActivity extends StylishActivity @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onInvalidKeyException(Exception e) { - showError(R.string.fingerprint_invalid_key); - removePrefsNoBackupKeys(); - e.printStackTrace(); - reInitWithSameFingerprintMode(); // restarts listening + showError(getString(R.string.fingerprint_invalid_key)); + deleteEntryKey(); } @RequiresApi(api = Build.VERSION_CODES.M) @Override public void onFingerPrintException(Exception e) { - showError(R.string.fingerprint_error); - e.printStackTrace(); - reInitWithSameFingerprintMode(); + showError(getString(R.string.fingerprint_error, e.getMessage())); + setFingerPrintView(e.getLocalizedMessage(), true); + } + + @RequiresApi(api = Build.VERSION_CODES.M) + private void deleteEntryKey() { + fingerPrintHelper.deleteEntryKey(); + removePrefsNoBackupKey(); + fingerPrintMode = NOT_CONFIGURED_MODE; + checkFingerprintAvailability(); } private void showError(final int messageId) { - runOnUiThread(() -> Toast.makeText(getApplicationContext(), messageId, Toast.LENGTH_SHORT).show()); + showError(getString(messageId)); } private void showError(final CharSequence message) { @@ -753,10 +779,7 @@ public class PasswordActivity extends StylishActivity break; case R.id.menu_fingerprint_remove_key: if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - fingerPrintHelper.deleteEntryKey(); - removePrefsNoBackupKeys(); - fingerPrintMode = NOT_CONFIGURED_MODE; - checkFingerprintAvailability(); + deleteEntryKey(); } break; default: @@ -793,11 +816,8 @@ public class PasswordActivity extends StylishActivity // Recheck fingerprint if error if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - //setEmptyViews(); - //mMessage = getString(R.string.fingerprint_error) + " : " + mMessage; - // TODO Change fingerprint message // Stay with the same mode - reInitWithSameFingerprintMode(); + reInitWithFingerprintMode(); } if (db.passwordEncodingError) { diff --git a/app/src/main/java/com/keepassdroid/settings/NestedSettingsFragment.java b/app/src/main/java/com/keepassdroid/settings/NestedSettingsFragment.java index affc89de5..cb1269262 100644 --- a/app/src/main/java/com/keepassdroid/settings/NestedSettingsFragment.java +++ b/app/src/main/java/com/keepassdroid/settings/NestedSettingsFragment.java @@ -131,16 +131,13 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat if (!fingerprintSupported) { // False if under Marshmallow fingerprintEnablePreference.setChecked(false); - fingerprintEnablePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - FragmentManager fragmentManager = getFragmentManager(); - assert fragmentManager != null; - ((SwitchPreference) preference).setChecked(false); - UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.M) - .show(getFragmentManager(), "unavailableFeatureDialog"); - return false; - } + fingerprintEnablePreference.setOnPreferenceClickListener(preference -> { + FragmentManager fragmentManager = getFragmentManager(); + assert fragmentManager != null; + ((SwitchPreference) preference).setChecked(false); + UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.M) + .show(getFragmentManager(), "unavailableFeatureDialog"); + return false; }); } @@ -148,45 +145,39 @@ public class NestedSettingsFragment extends PreferenceFragmentCompat if (!fingerprintSupported) { deleteKeysFingerprints.setEnabled(false); } else { - deleteKeysFingerprints.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - new AlertDialog.Builder(getContext()) - .setMessage(getResources().getString(R.string.fingerprint_delete_all_warning)) - .setIcon(getResources().getDrawable( - android.R.drawable.ic_dialog_alert)) - .setPositiveButton( - getResources().getString(android.R.string.yes), - new DialogInterface.OnClickListener() { - @RequiresApi(api = Build.VERSION_CODES.M) - @Override - public void onClick(DialogInterface dialog, - int which) { - FingerPrintHelper.deleteEntryKeyInKeystoreForFingerprints( - getContext(), - new FingerPrintHelper.FingerPrintErrorCallback() { - @Override - public void onInvalidKeyException(Exception e) { - } + deleteKeysFingerprints.setOnPreferenceClickListener(preference -> { + new AlertDialog.Builder(getContext()) + .setMessage(getResources().getString(R.string.fingerprint_delete_all_warning)) + .setIcon(getResources().getDrawable( + android.R.drawable.ic_dialog_alert)) + .setPositiveButton( + getResources().getString(android.R.string.yes), + new DialogInterface.OnClickListener() { + @RequiresApi(api = Build.VERSION_CODES.M) + @Override + public void onClick(DialogInterface dialog, + int which) { + FingerPrintHelper.deleteEntryKeyInKeystoreForFingerprints( + getContext(), + new FingerPrintHelper.FingerPrintErrorCallback() { + @Override + public void onInvalidKeyException(Exception e) {} - @Override - public void onFingerPrintException(Exception e) { - Toast.makeText(getContext(), R.string.fingerprint_error, Toast.LENGTH_SHORT).show(); - } - }); - PreferencesUtil.deleteAllValuesFromNoBackupPreferences(getContext()); - } - }) - .setNegativeButton( - getResources().getString(android.R.string.no), - new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, - int which) { - } - }).show(); - return false; - } + @Override + public void onFingerPrintException(Exception e) { + Toast.makeText(getContext(), + getString(R.string.fingerprint_error, e.getLocalizedMessage()), + Toast.LENGTH_SHORT).show(); + } + }); + PreferencesUtil.deleteAllValuesFromNoBackupPreferences(getContext()); + } + }) + .setNegativeButton( + getResources().getString(android.R.string.no), + (dialog, which) -> { + }).show(); + return false; }); } break; diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index eb1238bb7..2c6ccc6c8 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -188,9 +188,9 @@ Reconnaissance d\'empreinte digitale non configuré pour cet appareil Attente d\'une reconnaissance d\'empreinte digitale Mot de passe encrypté stocké - Problème de clé invalide + Problème de clé invalide. Restaurez votre mot de passe. Empreinte digitale non reconnue - Problème d\'empreinte digitale + Problème d\'empreinte digitale : %1$s Utiliser l\'empreinte digitale pour stocker le mot de passe Pas de mot de passe encore stocké pour cette base de données Historique diff --git a/app/src/main/res/values-hu/strings.xml b/app/src/main/res/values-hu/strings.xml index 02ece7439..39ada7f59 100644 --- a/app/src/main/res/values-hu/strings.xml +++ b/app/src/main/res/values-hu/strings.xml @@ -161,8 +161,8 @@ Az ujjlenyomat használat nincs még beállítva az eszközön Várakozás az ujjelnyomat megadására Titkosított jelszó elmentve - Érvénytelen kulcs - Érvénytelen ujjlenyomat + Érvénytelen kulcs. Helyezze vissza jelszavát. + Érvénytelen ujjlenyomat : %1$s Használjon ujjlenyomatot a jelszó elmentéséhez Nincs még jelszó beállítva ehhez az adatbázishoz diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0ebe07f66..78ecddc1a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -188,9 +188,9 @@ Fingerprint supported but not configured for device Listening for fingerprints Encrypted password stored - Invalid Key problem + Invalid fingerprint key problem. Restore your password. Fingerprint not recognized - Fingerprint problem + Fingerprint problem : %1$s Use fingerprint to store this password No password stored yet for this database History