mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Merge branch 'develop' into feature/Autofill_Save
This commit is contained in:
@@ -101,7 +101,7 @@ dependencies {
|
|||||||
// WARNING: Bug with extra field
|
// WARNING: Bug with extra field
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
implementation 'androidx.documentfile:documentfile:1.0.1'
|
implementation 'androidx.documentfile:documentfile:1.0.1'
|
||||||
implementation 'androidx.biometric:biometric:1.0.1'
|
implementation 'androidx.biometric:biometric:1.1.0-alpha02'
|
||||||
// Lifecycle - LiveData - ViewModel - Coroutines
|
// Lifecycle - LiveData - ViewModel - Coroutines
|
||||||
implementation "androidx.core:core-ktx:1.3.1"
|
implementation "androidx.core:core-ktx:1.3.1"
|
||||||
implementation 'androidx.fragment:fragment-ktx:1.2.5'
|
implementation 'androidx.fragment:fragment-ktx:1.2.5'
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
|
|||||||
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
||||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
|
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
|
||||||
|
import com.kunzisoft.keepass.biometric.BiometricUnlockDatabaseHelper
|
||||||
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
|
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
|
||||||
import com.kunzisoft.keepass.database.element.Database
|
import com.kunzisoft.keepass.database.element.Database
|
||||||
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
|
import com.kunzisoft.keepass.database.exception.DuplicateUuidDatabaseException
|
||||||
@@ -68,7 +69,6 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
|||||||
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
|
import com.kunzisoft.keepass.utils.BACK_PREVIOUS_KEYBOARD_ACTION
|
||||||
import com.kunzisoft.keepass.utils.MenuUtil
|
import com.kunzisoft.keepass.utils.MenuUtil
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
import com.kunzisoft.keepass.utils.closeDatabase
|
|
||||||
import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
|
import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
|
||||||
import com.kunzisoft.keepass.view.KeyFileSelectionView
|
import com.kunzisoft.keepass.view.KeyFileSelectionView
|
||||||
import com.kunzisoft.keepass.view.asError
|
import com.kunzisoft.keepass.view.asError
|
||||||
@@ -698,7 +698,7 @@ open class PasswordActivity : SpecialModeActivity() {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!readOnlyEducationPerformed) {
|
if (!readOnlyEducationPerformed) {
|
||||||
val biometricCanAuthenticate = BiometricManager.from(this).canAuthenticate()
|
val biometricCanAuthenticate = BiometricUnlockDatabaseHelper.canAuthenticate(this)
|
||||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
|
||||||
&& PreferencesUtil.isBiometricUnlockEnable(applicationContext)
|
&& PreferencesUtil.isBiometricUnlockEnable(applicationContext)
|
||||||
&& (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED || biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS)
|
&& (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED || biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS)
|
||||||
|
|||||||
@@ -53,6 +53,10 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
private var biometricUnlockDatabaseHelper: BiometricUnlockDatabaseHelper? = null
|
private var biometricUnlockDatabaseHelper: BiometricUnlockDatabaseHelper? = null
|
||||||
private var biometricMode: Mode = Mode.UNAVAILABLE
|
private var biometricMode: Mode = Mode.UNAVAILABLE
|
||||||
|
|
||||||
|
// Only to fix multiple fingerprint menu #332
|
||||||
|
private var mAllowAdvancedUnlockMenu = false
|
||||||
|
private var mAddBiometricMenuInProgress = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manage setting to auto open biometric prompt
|
* Manage setting to auto open biometric prompt
|
||||||
*/
|
*/
|
||||||
@@ -84,13 +88,15 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
|
|
||||||
// biometric not supported (by API level or hardware) so keep option hidden
|
// biometric not supported (by API level or hardware) so keep option hidden
|
||||||
// or manually disable
|
// or manually disable
|
||||||
val biometricCanAuthenticate = BiometricManager.from(context).canAuthenticate()
|
val biometricCanAuthenticate = BiometricUnlockDatabaseHelper.canAuthenticate(context)
|
||||||
allowOpenBiometricPrompt = true
|
allowOpenBiometricPrompt = true
|
||||||
|
|
||||||
if (!PreferencesUtil.isBiometricUnlockEnable(context)
|
if (!PreferencesUtil.isBiometricUnlockEnable(context)
|
||||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
||||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
|
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE) {
|
||||||
toggleMode(Mode.UNAVAILABLE)
|
toggleMode(Mode.UNAVAILABLE)
|
||||||
|
} else if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED){
|
||||||
|
toggleMode(Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED)
|
||||||
} else {
|
} else {
|
||||||
// biometric is available but not configured, show icon but in disabled state with some information
|
// biometric is available but not configured, show icon but in disabled state with some information
|
||||||
if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
|
if (biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED) {
|
||||||
@@ -114,7 +120,7 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
} else {
|
} else {
|
||||||
cipherDatabaseAction.containsCipherDatabase(databaseFileUri) { containsCipher ->
|
cipherDatabaseAction.containsCipherDatabase(databaseFileUri) { containsCipher ->
|
||||||
// biometric available but no stored password found yet for this DB so show info don't listen
|
// biometric available but no stored password found yet for this DB so show info don't listen
|
||||||
toggleMode( if (containsCipher) {
|
toggleMode(if (containsCipher) {
|
||||||
// listen for decryption
|
// listen for decryption
|
||||||
Mode.EXTRACT_CREDENTIAL
|
Mode.EXTRACT_CREDENTIAL
|
||||||
} else {
|
} else {
|
||||||
@@ -156,10 +162,16 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
|
||||||
context.runOnUiThread {
|
context.runOnUiThread {
|
||||||
when (biometricMode) {
|
when (biometricMode) {
|
||||||
Mode.UNAVAILABLE -> {}
|
Mode.UNAVAILABLE -> {
|
||||||
Mode.BIOMETRIC_NOT_CONFIGURED -> {}
|
}
|
||||||
Mode.KEY_MANAGER_UNAVAILABLE -> {}
|
Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED -> {
|
||||||
Mode.WAIT_CREDENTIAL -> {}
|
}
|
||||||
|
Mode.BIOMETRIC_NOT_CONFIGURED -> {
|
||||||
|
}
|
||||||
|
Mode.KEY_MANAGER_UNAVAILABLE -> {
|
||||||
|
}
|
||||||
|
Mode.WAIT_CREDENTIAL -> {
|
||||||
|
}
|
||||||
Mode.STORE_CREDENTIAL -> {
|
Mode.STORE_CREDENTIAL -> {
|
||||||
// newly store the entered password in encrypted way
|
// newly store the entered password in encrypted way
|
||||||
biometricUnlockDatabaseHelper?.encryptData(passwordView?.text.toString())
|
biometricUnlockDatabaseHelper?.encryptData(passwordView?.text.toString())
|
||||||
@@ -183,6 +195,15 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
advancedUnlockInfoView?.setIconViewClickListener(false, null)
|
advancedUnlockInfoView?.setIconViewClickListener(false, null)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initSecurityUpdateRequired() {
|
||||||
|
showFingerPrintViews(true)
|
||||||
|
setAdvancedUnlockedTitleView(R.string.biometric_security_update_required)
|
||||||
|
|
||||||
|
advancedUnlockInfoView?.setIconViewClickListener(false) {
|
||||||
|
context.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun initNotConfigured() {
|
private fun initNotConfigured() {
|
||||||
showFingerPrintViews(true)
|
showFingerPrintViews(true)
|
||||||
setAdvancedUnlockedTitleView(R.string.configure_biometric)
|
setAdvancedUnlockedTitleView(R.string.configure_biometric)
|
||||||
@@ -196,7 +217,6 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
private fun initKeyManagerNotAvailable() {
|
private fun initKeyManagerNotAvailable() {
|
||||||
showFingerPrintViews(true)
|
showFingerPrintViews(true)
|
||||||
setAdvancedUnlockedTitleView(R.string.keystore_not_accessible)
|
setAdvancedUnlockedTitleView(R.string.keystore_not_accessible)
|
||||||
setAdvancedUnlockedMessageView("")
|
|
||||||
|
|
||||||
advancedUnlockInfoView?.setIconViewClickListener(false) {
|
advancedUnlockInfoView?.setIconViewClickListener(false) {
|
||||||
context.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
|
context.startActivity(Intent(Settings.ACTION_SECURITY_SETTINGS))
|
||||||
@@ -210,17 +230,25 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
|
|
||||||
advancedUnlockInfoView?.setIconViewClickListener(false) {
|
advancedUnlockInfoView?.setIconViewClickListener(false) {
|
||||||
biometricAuthenticationCallback.onAuthenticationError(
|
biometricAuthenticationCallback.onAuthenticationError(
|
||||||
BiometricConstants.ERROR_UNABLE_TO_PROCESS
|
BiometricConstants.ERROR_UNABLE_TO_PROCESS, context.getString(R.string.credential_before_click_biometric_button))
|
||||||
, context.getString(R.string.credential_before_click_biometric_button))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openBiometricPrompt(biometricPrompt: BiometricPrompt?,
|
private fun openBiometricPrompt(biometricPrompt: BiometricPrompt?,
|
||||||
cryptoObject: BiometricPrompt.CryptoObject,
|
cryptoObject: BiometricPrompt.CryptoObject?,
|
||||||
promptInfo: BiometricPrompt.PromptInfo) {
|
promptInfo: BiometricPrompt.PromptInfo) {
|
||||||
context.runOnUiThread {
|
context.runOnUiThread {
|
||||||
if (allowOpenBiometricPrompt)
|
if (allowOpenBiometricPrompt) {
|
||||||
biometricPrompt?.authenticate(promptInfo, cryptoObject)
|
if (biometricPrompt != null) {
|
||||||
|
if (cryptoObject != null) {
|
||||||
|
biometricPrompt.authenticate(promptInfo, cryptoObject)
|
||||||
|
} else {
|
||||||
|
setAdvancedUnlockedTitleView(R.string.crypto_object_not_initialized)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setAdvancedUnlockedTitleView(R.string.biometric_prompt_not_initialized)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,14 +258,10 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
setAdvancedUnlockedMessageView("")
|
setAdvancedUnlockedMessageView("")
|
||||||
|
|
||||||
biometricUnlockDatabaseHelper?.initEncryptData { biometricPrompt, cryptoObject, promptInfo ->
|
biometricUnlockDatabaseHelper?.initEncryptData { biometricPrompt, cryptoObject, promptInfo ->
|
||||||
|
// Set listener to open the biometric dialog and save credential
|
||||||
cryptoObject?.let { crypto ->
|
advancedUnlockInfoView?.setIconViewClickListener { _ ->
|
||||||
// Set listener to open the biometric dialog and save credential
|
openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
|
||||||
advancedUnlockInfoView?.setIconViewClickListener { _ ->
|
|
||||||
openBiometricPrompt(biometricPrompt, crypto, promptInfo)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -252,19 +276,16 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
it?.specParameters?.let { specs ->
|
it?.specParameters?.let { specs ->
|
||||||
biometricUnlockDatabaseHelper?.initDecryptData(specs) { biometricPrompt, cryptoObject, promptInfo ->
|
biometricUnlockDatabaseHelper?.initDecryptData(specs) { biometricPrompt, cryptoObject, promptInfo ->
|
||||||
|
|
||||||
cryptoObject?.let { crypto ->
|
// Set listener to open the biometric dialog and check credential
|
||||||
// Set listener to open the biometric dialog and check credential
|
advancedUnlockInfoView?.setIconViewClickListener { _ ->
|
||||||
advancedUnlockInfoView?.setIconViewClickListener { _ ->
|
openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
|
||||||
openBiometricPrompt(biometricPrompt, crypto, promptInfo)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto open the biometric prompt
|
|
||||||
if (isBiometricPromptAutoOpenEnable) {
|
|
||||||
isBiometricPromptAutoOpenEnable = false
|
|
||||||
openBiometricPrompt(biometricPrompt, crypto, promptInfo)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto open the biometric prompt
|
||||||
|
if (isBiometricPromptAutoOpenEnable) {
|
||||||
|
isBiometricPromptAutoOpenEnable = false
|
||||||
|
openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -273,16 +294,32 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun initBiometricMode() {
|
fun initBiometricMode() {
|
||||||
|
mAllowAdvancedUnlockMenu = false
|
||||||
when (biometricMode) {
|
when (biometricMode) {
|
||||||
Mode.UNAVAILABLE -> initNotAvailable()
|
Mode.UNAVAILABLE -> initNotAvailable()
|
||||||
|
Mode.BIOMETRIC_SECURITY_UPDATE_REQUIRED -> initSecurityUpdateRequired()
|
||||||
Mode.BIOMETRIC_NOT_CONFIGURED -> initNotConfigured()
|
Mode.BIOMETRIC_NOT_CONFIGURED -> initNotConfigured()
|
||||||
Mode.KEY_MANAGER_UNAVAILABLE -> initKeyManagerNotAvailable()
|
Mode.KEY_MANAGER_UNAVAILABLE -> initKeyManagerNotAvailable()
|
||||||
Mode.WAIT_CREDENTIAL -> initWaitData()
|
Mode.WAIT_CREDENTIAL -> initWaitData()
|
||||||
Mode.STORE_CREDENTIAL -> initEncryptData()
|
Mode.STORE_CREDENTIAL -> initEncryptData()
|
||||||
Mode.EXTRACT_CREDENTIAL -> initDecryptData()
|
Mode.EXTRACT_CREDENTIAL -> initDecryptData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
invalidateBiometricMenu()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun invalidateBiometricMenu() {
|
||||||
// Show fingerprint key deletion
|
// Show fingerprint key deletion
|
||||||
context.invalidateOptionsMenu()
|
if (!mAddBiometricMenuInProgress) {
|
||||||
|
mAddBiometricMenuInProgress = true
|
||||||
|
cipherDatabaseAction.containsCipherDatabase(databaseFileUri) { containsCipher ->
|
||||||
|
mAllowAdvancedUnlockMenu = containsCipher
|
||||||
|
&& (biometricMode != Mode.UNAVAILABLE
|
||||||
|
&& biometricMode != Mode.KEY_MANAGER_UNAVAILABLE)
|
||||||
|
mAddBiometricMenuInProgress = false
|
||||||
|
context.invalidateOptionsMenu()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroy() {
|
fun destroy() {
|
||||||
@@ -293,26 +330,19 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
checkboxPasswordView?.setOnCheckedChangeListener(onCheckedPasswordChangeListener)
|
checkboxPasswordView?.setOnCheckedChangeListener(onCheckedPasswordChangeListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only to fix multiple fingerprint menu #332
|
|
||||||
private var addBiometricMenuInProgress = false
|
|
||||||
fun inflateOptionsMenu(menuInflater: MenuInflater, menu: Menu) {
|
fun inflateOptionsMenu(menuInflater: MenuInflater, menu: Menu) {
|
||||||
if (!addBiometricMenuInProgress) {
|
if (mAllowAdvancedUnlockMenu)
|
||||||
addBiometricMenuInProgress = true
|
menuInflater.inflate(R.menu.advanced_unlock, menu)
|
||||||
cipherDatabaseAction.containsCipherDatabase(databaseFileUri) {
|
|
||||||
if ((biometricMode != Mode.UNAVAILABLE && biometricMode != Mode.BIOMETRIC_NOT_CONFIGURED)
|
|
||||||
&& it) {
|
|
||||||
menuInflater.inflate(R.menu.advanced_unlock, menu)
|
|
||||||
addBiometricMenuInProgress = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun deleteEntryKey() {
|
fun deleteEntryKey() {
|
||||||
|
allowOpenBiometricPrompt = false
|
||||||
|
advancedUnlockInfoView?.setIconViewClickListener(false, null)
|
||||||
|
biometricUnlockDatabaseHelper?.closeBiometricPrompt()
|
||||||
biometricUnlockDatabaseHelper?.deleteEntryKey()
|
biometricUnlockDatabaseHelper?.deleteEntryKey()
|
||||||
cipherDatabaseAction.deleteByDatabaseUri(databaseFileUri)
|
cipherDatabaseAction.deleteByDatabaseUri(databaseFileUri) {
|
||||||
biometricMode = Mode.BIOMETRIC_NOT_CONFIGURED
|
checkBiometricAvailability()
|
||||||
checkBiometricAvailability()
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun handleEncryptedResult(encryptedValue: String, ivSpec: String) {
|
override fun handleEncryptedResult(encryptedValue: String, ivSpec: String) {
|
||||||
@@ -359,7 +389,13 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
|||||||
}
|
}
|
||||||
|
|
||||||
enum class Mode {
|
enum class Mode {
|
||||||
UNAVAILABLE, BIOMETRIC_NOT_CONFIGURED, KEY_MANAGER_UNAVAILABLE, WAIT_CREDENTIAL, STORE_CREDENTIAL, EXTRACT_CREDENTIAL
|
UNAVAILABLE,
|
||||||
|
BIOMETRIC_SECURITY_UPDATE_REQUIRED,
|
||||||
|
BIOMETRIC_NOT_CONFIGURED,
|
||||||
|
KEY_MANAGER_UNAVAILABLE,
|
||||||
|
WAIT_CREDENTIAL,
|
||||||
|
STORE_CREDENTIAL,
|
||||||
|
EXTRACT_CREDENTIAL
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ import android.util.Base64
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.biometric.BiometricManager
|
import androidx.biometric.BiometricManager
|
||||||
|
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG
|
||||||
|
import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK
|
||||||
|
import androidx.biometric.BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
||||||
import androidx.biometric.BiometricPrompt
|
import androidx.biometric.BiometricPrompt
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentActivity
|
||||||
import com.kunzisoft.keepass.R
|
import com.kunzisoft.keepass.R
|
||||||
@@ -66,7 +69,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
|||||||
setDeviceCredentialAllowed(true)
|
setDeviceCredentialAllowed(true)
|
||||||
else
|
else
|
||||||
*/
|
*/
|
||||||
setNegativeButtonText(context.getString(android.R.string.cancel))
|
setNegativeButtonText(context.getString(android.R.string.cancel))
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
private val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder().apply {
|
private val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder().apply {
|
||||||
@@ -78,8 +81,8 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
|||||||
if (keyguardManager?.isDeviceSecure == true)
|
if (keyguardManager?.isDeviceSecure == true)
|
||||||
setDeviceCredentialAllowed(true)
|
setDeviceCredentialAllowed(true)
|
||||||
else
|
else
|
||||||
*/
|
*/
|
||||||
setNegativeButtonText(context.getString(android.R.string.cancel))
|
setNegativeButtonText(context.getString(android.R.string.cancel))
|
||||||
}.build()
|
}.build()
|
||||||
|
|
||||||
val isKeyManagerInitialized: Boolean
|
val isKeyManagerInitialized: Boolean
|
||||||
@@ -91,7 +94,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
if (BiometricManager.from(context).canAuthenticate() != BiometricManager.BIOMETRIC_SUCCESS) {
|
if (canAuthenticate(context) != BiometricManager.BIOMETRIC_SUCCESS) {
|
||||||
// really not much to do when no fingerprint support found
|
// really not much to do when no fingerprint support found
|
||||||
isKeyManagerInit = false
|
isKeyManagerInit = false
|
||||||
} else {
|
} else {
|
||||||
@@ -158,7 +161,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
|||||||
fun initEncryptData(actionIfCypherInit
|
fun initEncryptData(actionIfCypherInit
|
||||||
: (biometricPrompt: BiometricPrompt?,
|
: (biometricPrompt: BiometricPrompt?,
|
||||||
cryptoObject: BiometricPrompt.CryptoObject?,
|
cryptoObject: BiometricPrompt.CryptoObject?,
|
||||||
promptInfo: BiometricPrompt.PromptInfo)->Unit) {
|
promptInfo: BiometricPrompt.PromptInfo) -> Unit) {
|
||||||
if (!isKeyManagerInitialized) {
|
if (!isKeyManagerInitialized) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -203,9 +206,9 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun initDecryptData(ivSpecValue: String, actionIfCypherInit
|
fun initDecryptData(ivSpecValue: String, actionIfCypherInit
|
||||||
: (biometricPrompt: BiometricPrompt?,
|
: (biometricPrompt: BiometricPrompt?,
|
||||||
cryptoObject: BiometricPrompt.CryptoObject?,
|
cryptoObject: BiometricPrompt.CryptoObject?,
|
||||||
promptInfo: BiometricPrompt.PromptInfo)->Unit) {
|
promptInfo: BiometricPrompt.PromptInfo) -> Unit) {
|
||||||
if (!isKeyManagerInitialized) {
|
if (!isKeyManagerInitialized) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -295,6 +298,20 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
|||||||
private const val BIOMETRIC_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC
|
private const val BIOMETRIC_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC
|
||||||
private const val BIOMETRIC_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
|
private const val BIOMETRIC_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
|
||||||
|
|
||||||
|
fun canAuthenticate(context: Context): Int {
|
||||||
|
return try {
|
||||||
|
BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to authenticate with strong biometric.", e)
|
||||||
|
try {
|
||||||
|
BiometricManager.from(context).canAuthenticate(BIOMETRIC_WEAK)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(TAG, "Unable to authenticate with weak biometric.", e)
|
||||||
|
BIOMETRIC_ERROR_HW_UNAVAILABLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove entry key in keystore
|
* Remove entry key in keystore
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ import com.kunzisoft.keepass.icons.IconPackChooser
|
|||||||
import com.kunzisoft.keepass.settings.preference.IconPackListPreference
|
import com.kunzisoft.keepass.settings.preference.IconPackListPreference
|
||||||
import com.kunzisoft.keepass.utils.UriUtil
|
import com.kunzisoft.keepass.utils.UriUtil
|
||||||
|
|
||||||
|
|
||||||
class NestedAppSettingsFragment : NestedSettingsFragment() {
|
class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||||
|
|
||||||
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
|
override fun onCreateScreenPreference(screen: Screen, savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
@@ -211,7 +212,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
// < M solve verifyError exception
|
// < M solve verifyError exception
|
||||||
var biometricUnlockSupported = false
|
var biometricUnlockSupported = false
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||||
val biometricCanAuthenticate = BiometricManager.from(activity).canAuthenticate()
|
val biometricCanAuthenticate = BiometricUnlockDatabaseHelper.canAuthenticate(activity)
|
||||||
biometricUnlockSupported = biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|
biometricUnlockSupported = biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|
||||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|
||||||
}
|
}
|
||||||
@@ -336,8 +337,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
|||||||
|
|
||||||
activity?.let { activity ->
|
activity?.let { activity ->
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
findPreference<SwitchPreference?>(getString(R.string.settings_autofill_enable_key))?.let {
|
findPreference<SwitchPreference?>(getString(R.string.settings_autofill_enable_key))?.let { autoFillEnablePreference ->
|
||||||
autoFillEnablePreference ->
|
|
||||||
val autofillManager = activity.getSystemService(AutofillManager::class.java)
|
val autofillManager = activity.getSystemService(AutofillManager::class.java)
|
||||||
autoFillEnablePreference.isChecked = autofillManager != null
|
autoFillEnablePreference.isChecked = autofillManager != null
|
||||||
&& autofillManager.hasEnabledAutofillServices()
|
&& autofillManager.hasEnabledAutofillServices()
|
||||||
|
|||||||
@@ -268,6 +268,7 @@
|
|||||||
<string name="version_label">Version %1$s</string>
|
<string name="version_label">Version %1$s</string>
|
||||||
<string name="build_label">Build %1$s</string>
|
<string name="build_label">Build %1$s</string>
|
||||||
<string name="configure_biometric">Biometric prompt is supported, but not set up.</string>
|
<string name="configure_biometric">Biometric prompt is supported, but not set up.</string>
|
||||||
|
<string name="biometric_security_update_required">Biometric security update required.</string>
|
||||||
<string name="keystore_not_accessible">The keystore is not properly initialized.</string>
|
<string name="keystore_not_accessible">The keystore is not properly initialized.</string>
|
||||||
<string name="open_biometric_prompt_unlock_database">Open the biometric prompt to unlock the database</string>
|
<string name="open_biometric_prompt_unlock_database">Open the biometric prompt to unlock the database</string>
|
||||||
<string name="open_biometric_prompt_store_credential">Open the biometric prompt to store credentials</string>
|
<string name="open_biometric_prompt_store_credential">Open the biometric prompt to store credentials</string>
|
||||||
@@ -280,6 +281,8 @@
|
|||||||
<string name="biometric_not_recognized">Could not recognize biometric</string>
|
<string name="biometric_not_recognized">Could not recognize biometric</string>
|
||||||
<string name="biometric_scanning_error">Biometric error: %1$s</string>
|
<string name="biometric_scanning_error">Biometric error: %1$s</string>
|
||||||
<string name="no_credentials_stored">This database does not have stored credential yet.</string>
|
<string name="no_credentials_stored">This database does not have stored credential yet.</string>
|
||||||
|
<string name="biometric_prompt_not_initialized">Unable to initialize biometric prompt.</string>
|
||||||
|
<string name="crypto_object_not_initialized">Unable to retrieve crypto object.</string>
|
||||||
<string name="credential_before_click_biometric_button">Type in the password, and then click the \"Biometric\" button.</string>
|
<string name="credential_before_click_biometric_button">Type in the password, and then click the \"Biometric\" button.</string>
|
||||||
<string name="database_history">History</string>
|
<string name="database_history">History</string>
|
||||||
<string name="menu_appearance_settings">Appearance</string>
|
<string name="menu_appearance_settings">Appearance</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user