Quellcode durchsuchen

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 vor 4 Jahren
Ursprung
Commit
09b40cdec7
3 geänderte Dateien mit 39 neuen und 15 gelöschten Zeilen
  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 Datei anzeigen

@@ -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 Datei anzeigen

@@ -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 Datei anzeigen

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


Laden…
Abbrechen
Speichern