Bläddra i källkod

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>
master
Jason A. Donenfeld 4 år sedan
förälder
incheckning
09b40cdec7
3 ändrade filer med 39 tillägg och 15 borttagningar
  1. +10
    -5
      ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt
  2. +2
    -1
      ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt
  3. +27
    -9
      ui/src/main/java/com/wireguard/android/util/BiometricAuthenticator.kt

+ 10
- 5
ui/src/main/java/com/wireguard/android/fragment/TunnelEditorFragment.kt Visa fil

@@ -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
}



+ 2
- 1
ui/src/main/java/com/wireguard/android/preference/ZipExporterPreference.kt Visa fil

@@ -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 -> {


+ 27
- 9
ui/src/main/java/com/wireguard/android/util/BiometricAuthenticator.kt Visa fil

@@ -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)


Laddar…
Avbryt
Spara