From 5aed8223a93196364612acae2b9b5b9219b43d69 Mon Sep 17 00:00:00 2001 From: M66B Date: Wed, 1 Feb 2023 19:50:42 +0100 Subject: [PATCH] Require bio for showing password --- .../eu/faircode/email/FragmentAccount.java | 7 +- .../eu/faircode/email/FragmentIdentity.java | 8 +- .../java/eu/faircode/email/FragmentPop.java | 7 +- .../main/java/eu/faircode/email/Helper.java | 79 ++++++++++++++++--- 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/eu/faircode/email/FragmentAccount.java b/app/src/main/java/eu/faircode/email/FragmentAccount.java index 2ce38add88..b550010697 100644 --- a/app/src/main/java/eu/faircode/email/FragmentAccount.java +++ b/app/src/main/java/eu/faircode/email/FragmentAccount.java @@ -20,7 +20,6 @@ package eu.faircode.email; */ import static android.app.Activity.RESULT_OK; -import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE; import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE; import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_GMAIL; import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_OAUTH; @@ -614,7 +613,11 @@ public class FragmentAccount extends FragmentBase { rgEncryption.setVisibility(View.GONE); cbInsecure.setVisibility(View.GONE); - tilPassword.setEndIconMode(id < 0 || Helper.isSecure(getContext()) ? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE); + + if (id < 0) + tilPassword.setEndIconMode(END_ICON_PASSWORD_TOGGLE); + else + Helper.setupPasswordToggle(getActivity(), tilPassword); btnAdvanced.setVisibility(View.GONE); diff --git a/app/src/main/java/eu/faircode/email/FragmentIdentity.java b/app/src/main/java/eu/faircode/email/FragmentIdentity.java index 79a34f390a..73db00c9d0 100644 --- a/app/src/main/java/eu/faircode/email/FragmentIdentity.java +++ b/app/src/main/java/eu/faircode/email/FragmentIdentity.java @@ -20,7 +20,6 @@ package eu.faircode.email; */ import static android.app.Activity.RESULT_OK; -import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE; import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE; import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_OAUTH; import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD; @@ -69,7 +68,6 @@ import java.io.FileNotFoundException; import java.net.UnknownHostException; import java.security.cert.X509Certificate; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Objects; @@ -527,7 +525,11 @@ public class FragmentIdentity extends FragmentBase { btnAutoConfig.setEnabled(false); pbAutoConfig.setVisibility(View.GONE); cbInsecure.setVisibility(View.GONE); - tilPassword.setEndIconMode(id < 0 || Helper.isSecure(getContext()) ? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE); + + if (id < 0) + tilPassword.setEndIconMode(END_ICON_PASSWORD_TOGGLE); + else + Helper.setupPasswordToggle(getActivity(), tilPassword); btnAdvanced.setVisibility(View.GONE); diff --git a/app/src/main/java/eu/faircode/email/FragmentPop.java b/app/src/main/java/eu/faircode/email/FragmentPop.java index 3db67ed213..d39e468540 100644 --- a/app/src/main/java/eu/faircode/email/FragmentPop.java +++ b/app/src/main/java/eu/faircode/email/FragmentPop.java @@ -20,7 +20,6 @@ package eu.faircode.email; */ import static android.app.Activity.RESULT_OK; -import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE; import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE; import static eu.faircode.email.ServiceAuthenticator.AUTH_TYPE_PASSWORD; @@ -320,7 +319,11 @@ public class FragmentPop extends FragmentBase { // Initialize Helper.setViewsEnabled(view, false); - tilPassword.setEndIconMode(id < 0 || Helper.isSecure(getContext()) ? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE); + if (id < 0) + tilPassword.setEndIconMode(END_ICON_PASSWORD_TOGGLE); + else + Helper.setupPasswordToggle(getActivity(), tilPassword); + pbSave.setVisibility(View.GONE); grpError.setVisibility(View.GONE); diff --git a/app/src/main/java/eu/faircode/email/Helper.java b/app/src/main/java/eu/faircode/email/Helper.java index e45cdfe940..638649a2bb 100644 --- a/app/src/main/java/eu/faircode/email/Helper.java +++ b/app/src/main/java/eu/faircode/email/Helper.java @@ -22,6 +22,9 @@ package eu.faircode.email; import static android.os.Process.THREAD_PRIORITY_BACKGROUND; import static androidx.browser.customtabs.CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION; +import static com.google.android.material.textfield.TextInputLayout.END_ICON_NONE; +import static com.google.android.material.textfield.TextInputLayout.END_ICON_PASSWORD_TOGGLE; + import android.Manifest; import android.animation.Animator; import android.animation.ObjectAnimator; @@ -73,6 +76,8 @@ import android.text.Spannable; import android.text.TextUtils; import android.text.format.DateUtils; import android.text.format.Time; +import android.text.method.PasswordTransformationMethod; +import android.text.method.TransformationMethod; import android.util.DisplayMetrics; import android.util.Pair; import android.util.TypedValue; @@ -125,6 +130,7 @@ import androidx.viewpager.widget.PagerAdapter; import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.textfield.TextInputLayout; import org.openintents.openpgp.util.OpenPgpApi; @@ -2725,14 +2731,13 @@ public class Helper { } static boolean canAuthenticate(Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - String pin = prefs.getString("pin", null); - if (!TextUtils.isEmpty(pin)) - return true; - try { BiometricManager bm = BiometricManager.from(context); - return (bm.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS); + if (bm.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS) + return true; + if (bm.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) + return true; + return false; } catch (Throwable ex) { /* java.lang.SecurityException: eu.faircode.email from uid 10377 not allowed to perform USE_FINGERPRINT @@ -2802,13 +2807,14 @@ public class Helper { BiometricPrompt.PromptInfo.Builder info = new BiometricPrompt.PromptInfo.Builder() .setTitle(activity.getString(enabled == null ? R.string.app_name : R.string.title_setup_biometrics)); - KeyguardManager kgm = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && kgm != null && kgm.isDeviceSecure()) - info.setDeviceCredentialAllowed(true); + BiometricManager bm = BiometricManager.from(activity); + int authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK; + if (bm.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) + authenticators |= BiometricManager.Authenticators.DEVICE_CREDENTIAL; else info.setNegativeButtonText(activity.getString(android.R.string.cancel)); - - info.setConfirmationRequired(false); + info.setAllowedAuthenticators(authenticators) + .setConfirmationRequired(false); info.setSubtitle(activity.getString(enabled == null ? R.string.title_setup_biometrics_unlock : enabled @@ -3036,6 +3042,57 @@ public class Helper { prefs.edit().remove("last_authentication").apply(); } + static void setupPasswordToggle(FragmentActivity activity, TextInputLayout tilPassword) { + boolean can = canAuthenticate(activity); + boolean secure = isSecure(activity); + + tilPassword.setEndIconMode(can || secure ? END_ICON_PASSWORD_TOGGLE : END_ICON_NONE); + tilPassword.setEndIconOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + TransformationMethod tm = tilPassword.getEditText().getTransformationMethod(); + if (tm == null) + tilPassword.getEditText().setTransformationMethod(PasswordTransformationMethod.getInstance()); + else { + if (can) { + BiometricPrompt.PromptInfo.Builder info = new BiometricPrompt.PromptInfo.Builder() + .setTitle(activity.getString(R.string.title_setup_biometrics)) + .setSubtitle(activity.getString(R.string.title_password)); + + BiometricManager bm = BiometricManager.from(activity); + int authenticators = BiometricManager.Authenticators.BIOMETRIC_WEAK; + if (bm.canAuthenticate(BiometricManager.Authenticators.DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) + authenticators |= BiometricManager.Authenticators.DEVICE_CREDENTIAL; + else + info.setNegativeButtonText(activity.getString(android.R.string.cancel)); + info.setAllowedAuthenticators(authenticators) + .setConfirmationRequired(false); + + BiometricPrompt prompt = new BiometricPrompt(activity, Helper.getUIExecutor(), + new BiometricPrompt.AuthenticationCallback() { + @Override + public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { + tilPassword.post(new RunnableEx("tilPassword") { + @Override + protected void delegate() { + tilPassword.getEditText().setTransformationMethod(null); + } + }); + } + + @Override + public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { + ToastEx.makeText(activity, "Error " + errorCode + ": " + errString, Toast.LENGTH_LONG).show(); + } + }); + prompt.authenticate(info.build()); + } else if (secure) + tilPassword.getEditText().setTransformationMethod(null); + } + } + }); + } + static void selectKeyAlias(final Activity activity, final LifecycleOwner owner, final String alias, final IKeyAlias intf) { final Context context = activity.getApplicationContext(); new Thread(new Runnable() {