aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/ui
diff options
context:
space:
mode:
authorJason A. Donenfeld <Jason@zx2c4.com>2020-03-30 00:45:41 -0600
committerJason A. Donenfeld <Jason@zx2c4.com>2020-03-30 03:23:32 -0600
commit09b40cdec7d096afac11d42d194934fa7c011ab1 (patch)
tree78216aaa463f35795c1755a6c0ab0a1d15e55550 /ui
parentBiometricAuthenticator: implement biometric authentication for sensitive operations (diff)
downloadwireguard-android-09b40cdec7d096afac11d42d194934fa7c011ab1.tar.xz
wireguard-android-09b40cdec7d096afac11d42d194934fa7c011ab1.zip
BiometricAuthenticator: rework logic and bugs
Otherwise there's a frameworks bug that causes the fragment's activity to become null. Signed-off-by: Jason A. Donenfeld <Jason@zx2c4.com>
Diffstat (limited to 'ui')
-rw-r--r--ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt15
-rw-r--r--ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt3
-rw-r--r--ui/src/main/java/com/wireguard/android/util/BiometricAuthenticator.kt36
3 files changed, 39 insertions, 15 deletions
diff --git a/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt b/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt
index 1b8af50e..1c0cf2bd 100644
--- a/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt
+++ b/ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt
@@ -4,6 +4,7 @@
*/
package com.wireguard.android.fragment
+import android.app.Activity
import android.content.Context
import android.os.Bundle
import android.text.InputType
@@ -83,7 +84,7 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
}
override fun onDestroyView() {
- requireActivity().window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
+ activity?.window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
binding = null
super.onDestroyView()
}
@@ -106,7 +107,7 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
InputMethodManager.HIDE_NOT_ALWAYS)
}
// Tell the activity to finish itself or go back to the detail view.
- requireActivity().runOnUiThread {
+ activity.runOnUiThread {
// TODO(smaeul): Remove this hack when fixing the Config ViewModel
// The selected tunnel has to actually change, but we have to remember this one.
val savedTunnel = tunnel
@@ -228,13 +229,17 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
super.onViewStateRestored(savedInstanceState)
}
+ private var showingAuthenticator = false
+
fun onKeyClick(view: View) = onKeyFocusChange(view, true)
fun onKeyFocusChange(view: View, isFocused: Boolean) {
- if (!isFocused) return
+ if (!isFocused || showingAuthenticator) return
val edit = view as? EditText ?: return
if (!haveShownKeys && edit.text.isNotEmpty()) {
- BiometricAuthenticator.authenticate(R.string.biometric_prompt_private_key_title, requireActivity()) {
+ showingAuthenticator = true
+ BiometricAuthenticator.authenticate(R.string.biometric_prompt_private_key_title, this) {
+ showingAuthenticator = false
when (it) {
is BiometricAuthenticator.Result.Success, is BiometricAuthenticator.Result.HardwareUnavailableOrDisabled -> {
haveShownKeys = true
@@ -255,7 +260,7 @@ class TunnelEditorFragment : BaseFragment(), AppExclusionListener {
}
private fun showPrivateKey(edit: EditText) {
- requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
+ activity?.window?.addFlags(WindowManager.LayoutParams.FLAG_SECURE)
edit.inputType = InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
}
diff --git a/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt b/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
index c83c1cca..8ddce2b1 100644
--- a/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
+++ b/ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
@@ -83,7 +83,8 @@ class ZipExporterPreference(context: Context, attrs: AttributeSet?) : Preference
override fun onClick() {
val prefActivity = FragmentUtils.getPrefActivity(this)
- BiometricAuthenticator.authenticate(R.string.biometric_prompt_zip_exporter_title, prefActivity) {
+ val fragment = prefActivity.supportFragmentManager.fragments.first()
+ BiometricAuthenticator.authenticate(R.string.biometric_prompt_zip_exporter_title, fragment) {
when (it) {
// When we have successful authentication, or when there is no biometric hardware available.
is BiometricAuthenticator.Result.Success, is BiometricAuthenticator.Result.HardwareUnavailableOrDisabled -> {
diff --git a/ui/src/main/java/com/wireguard/android/util/BiometricAuthenticator.kt b/ui/src/main/java/com/wireguard/android/util/BiometricAuthenticator.kt
index cf81f768..aed8a4f2 100644
--- a/ui/src/main/java/com/wireguard/android/util/BiometricAuthenticator.kt
+++ b/ui/src/main/java/com/wireguard/android/util/BiometricAuthenticator.kt
@@ -5,15 +5,21 @@
package com.wireguard.android.util
+import android.annotation.SuppressLint
+import android.app.KeyguardManager
+import android.content.Context
+import android.os.Build
import android.os.Handler
import android.util.Log
import androidx.annotation.StringRes
import androidx.biometric.BiometricConstants
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
-import androidx.fragment.app.FragmentActivity
+import androidx.core.content.getSystemService
+import androidx.fragment.app.Fragment
import com.wireguard.android.R
+
object BiometricAuthenticator {
private const val TAG = "WireGuard/BiometricAuthenticator"
private val handler = Handler()
@@ -25,12 +31,25 @@ object BiometricAuthenticator {
object Cancelled : Result()
}
+ @SuppressLint("PrivateApi")
+ private fun isPinEnabled(context: Context): Boolean {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
+ return context.getSystemService<KeyguardManager>()!!.isDeviceSecure
+ return try {
+ val lockUtilsClass = Class.forName("com.android.internal.widget.LockPatternUtils")
+ val lockUtils = lockUtilsClass.getConstructor(Context::class.java).newInstance(context)
+ val method = lockUtilsClass.getMethod("isLockScreenDisabled")
+ !(method.invoke(lockUtils) as Boolean)
+ } catch (e: Exception) {
+ false
+ }
+ }
+
fun authenticate(
@StringRes dialogTitleRes: Int,
- fragmentActivity: FragmentActivity,
+ fragment: Fragment,
callback: (Result) -> Unit
) {
- val biometricManager = BiometricManager.from(fragmentActivity)
val authCallback = object : BiometricPrompt.AuthenticationCallback() {
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
super.onAuthenticationError(errorCode, errString)
@@ -44,13 +63,13 @@ object BiometricAuthenticator {
BiometricConstants.ERROR_NO_BIOMETRICS, BiometricConstants.ERROR_NO_DEVICE_CREDENTIAL -> {
Result.HardwareUnavailableOrDisabled
}
- else -> Result.Failure(errorCode, fragmentActivity.getString(R.string.biometric_auth_error_reason, errString))
+ else -> Result.Failure(errorCode, fragment.getString(R.string.biometric_auth_error_reason, errString))
})
}
override fun onAuthenticationFailed() {
super.onAuthenticationFailed()
- callback(Result.Failure(null, fragmentActivity.getString(R.string.biometric_auth_error)))
+ callback(Result.Failure(null, fragment.getString(R.string.biometric_auth_error)))
}
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
@@ -58,13 +77,12 @@ object BiometricAuthenticator {
callback(Result.Success(result.cryptoObject))
}
}
- val biometricPrompt = BiometricPrompt(fragmentActivity, { handler.post(it) }, authCallback)
+ val biometricPrompt = BiometricPrompt(fragment, { handler.post(it) }, authCallback)
val promptInfo = BiometricPrompt.PromptInfo.Builder()
- .setTitle(fragmentActivity.getString(dialogTitleRes))
+ .setTitle(fragment.getString(dialogTitleRes))
.setDeviceCredentialAllowed(true)
.build()
-
- if (biometricManager.canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS) {
+ if (BiometricManager.from(fragment.requireContext()).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS || isPinEnabled(fragment.requireContext())) {
biometricPrompt.authenticate(promptInfo)
} else {
callback(Result.HardwareUnavailableOrDisabled)