mirror of
https://github.com/Kunzisoft/KeePassDX.git
synced 2025-12-04 15:49:33 +01:00
Refactoring of advanced unlock
This commit is contained in:
@@ -50,7 +50,7 @@ import com.kunzisoft.keepass.activities.lock.LockingActivity
|
||||
import com.kunzisoft.keepass.activities.selection.SpecialModeActivity
|
||||
import com.kunzisoft.keepass.app.database.CipherDatabaseEntity
|
||||
import com.kunzisoft.keepass.autofill.AutofillHelper
|
||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockedManager
|
||||
import com.kunzisoft.keepass.biometric.AdvancedUnlockManager
|
||||
import com.kunzisoft.keepass.biometric.BiometricUnlockDatabaseHelper
|
||||
import com.kunzisoft.keepass.database.action.ProgressDatabaseTaskProvider
|
||||
import com.kunzisoft.keepass.database.element.Database
|
||||
@@ -114,7 +114,7 @@ open class PasswordActivity : SpecialModeActivity() {
|
||||
|
||||
private var mProgressDatabaseTaskProvider: ProgressDatabaseTaskProvider? = null
|
||||
|
||||
private var advancedUnlockedManager: AdvancedUnlockedManager? = null
|
||||
private var advancedUnlockManager: AdvancedUnlockManager? = null
|
||||
private var mAllowAutoOpenBiometricPrompt: Boolean = true
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
@@ -210,7 +210,7 @@ open class PasswordActivity : SpecialModeActivity() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (PreferencesUtil.isAdvancedUnlockEnable(this@PasswordActivity)) {
|
||||
// Stay with the same mode and init it
|
||||
advancedUnlockedManager?.initAdvancedUnlockMode()
|
||||
advancedUnlockManager?.initAdvancedUnlockMode()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,9 +388,9 @@ open class PasswordActivity : SpecialModeActivity() {
|
||||
// Init Biometric elements
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
if (PreferencesUtil.isAdvancedUnlockEnable(this)) {
|
||||
if (advancedUnlockedManager == null
|
||||
if (advancedUnlockManager == null
|
||||
&& databaseFileUri != null) {
|
||||
advancedUnlockedManager = AdvancedUnlockedManager(this,
|
||||
advancedUnlockManager = AdvancedUnlockManager(this,
|
||||
databaseFileUri,
|
||||
advancedUnlockInfoView,
|
||||
checkboxPasswordView,
|
||||
@@ -415,16 +415,16 @@ open class PasswordActivity : SpecialModeActivity() {
|
||||
}
|
||||
})
|
||||
}
|
||||
advancedUnlockedManager?.isBiometricPromptAutoOpenEnable =
|
||||
advancedUnlockManager?.isBiometricPromptAutoOpenEnable =
|
||||
mAllowAutoOpenBiometricPrompt && mProgressDatabaseTaskProvider?.isBinded() != true
|
||||
advancedUnlockedManager?.checkBiometricAvailability()
|
||||
advancedUnlockManager?.checkBiometricAvailability()
|
||||
} else {
|
||||
advancedUnlockInfoView?.visibility = View.GONE
|
||||
advancedUnlockedManager?.destroy()
|
||||
advancedUnlockedManager = null
|
||||
advancedUnlockManager?.destroy()
|
||||
advancedUnlockManager = null
|
||||
}
|
||||
}
|
||||
if (advancedUnlockedManager == null) {
|
||||
if (advancedUnlockManager == null) {
|
||||
checkboxPasswordView?.setOnCheckedChangeListener(enableButtonOnCheckedChangeListener)
|
||||
}
|
||||
checkboxKeyFileView?.setOnCheckedChangeListener(enableButtonOnCheckedChangeListener)
|
||||
@@ -480,8 +480,8 @@ open class PasswordActivity : SpecialModeActivity() {
|
||||
mProgressDatabaseTaskProvider?.unregisterProgressTask()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
advancedUnlockedManager?.destroy()
|
||||
advancedUnlockedManager = null
|
||||
advancedUnlockManager?.destroy()
|
||||
advancedUnlockManager = null
|
||||
}
|
||||
|
||||
// Reinit locking activity UI variable
|
||||
@@ -594,7 +594,7 @@ open class PasswordActivity : SpecialModeActivity() {
|
||||
|
||||
if ( Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
// biometric menu
|
||||
advancedUnlockedManager?.inflateOptionsMenu(inflater, menu)
|
||||
advancedUnlockManager?.inflateOptionsMenu(inflater, menu)
|
||||
}
|
||||
|
||||
super.onCreateOptionsMenu(menu)
|
||||
@@ -709,7 +709,7 @@ open class PasswordActivity : SpecialModeActivity() {
|
||||
changeOpenFileReadIcon(item)
|
||||
}
|
||||
R.id.menu_keystore_remove_key -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
advancedUnlockedManager?.deleteEncryptedDatabaseKey()
|
||||
advancedUnlockManager?.deleteEncryptedDatabaseKey()
|
||||
}
|
||||
else -> return MenuUtil.onDefaultMenuOptionsItemSelected(this, item)
|
||||
}
|
||||
@@ -725,6 +725,11 @@ open class PasswordActivity : SpecialModeActivity() {
|
||||
|
||||
mAllowAutoOpenBiometricPrompt = false
|
||||
|
||||
// To get device credential unlock result
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
// TODO Advanced unlock response
|
||||
}
|
||||
|
||||
// To get entry in result
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
AutofillHelper.onActivityResultSetResultAndFinish(this, requestCode, resultCode, data)
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
package com.kunzisoft.keepass.biometric
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import javax.crypto.Cipher
|
||||
|
||||
data class AdvancedUnlockCryptoPrompt(var cipher: Cipher,
|
||||
@StringRes var promptTitleId: Int,
|
||||
@StringRes var promptDescriptionId: Int? = null,
|
||||
var isDeviceCredentialOperation: Boolean,
|
||||
var isBiometricOperation: Boolean)
|
||||
@@ -40,14 +40,14 @@ import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
import com.kunzisoft.keepass.view.AdvancedUnlockInfoView
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
class AdvancedUnlockedManager(var context: FragmentActivity,
|
||||
var databaseFileUri: Uri,
|
||||
private var advancedUnlockInfoView: AdvancedUnlockInfoView?,
|
||||
private var checkboxPasswordView: CompoundButton?,
|
||||
private var onCheckedPasswordChangeListener: CompoundButton.OnCheckedChangeListener? = null,
|
||||
var passwordView: TextView?,
|
||||
private var loadDatabaseAfterRegisterCredentials: (encryptedPassword: String?, ivSpec: String?) -> Unit,
|
||||
private var loadDatabaseAfterRetrieveCredentials: (decryptedPassword: String?) -> Unit)
|
||||
class AdvancedUnlockManager(var context: FragmentActivity,
|
||||
var databaseFileUri: Uri,
|
||||
private var advancedUnlockInfoView: AdvancedUnlockInfoView?,
|
||||
private var checkboxPasswordView: CompoundButton?,
|
||||
private var onCheckedPasswordChangeListener: CompoundButton.OnCheckedChangeListener? = null,
|
||||
var passwordView: TextView?,
|
||||
private var loadDatabaseAfterRegisterCredentials: (encryptedPassword: String?, ivSpec: String?) -> Unit,
|
||||
private var loadDatabaseAfterRetrieveCredentials: (decryptedPassword: String?) -> Unit)
|
||||
: BiometricUnlockDatabaseHelper.BiometricUnlockCallback {
|
||||
|
||||
private var biometricUnlockDatabaseHelper: BiometricUnlockDatabaseHelper? = null
|
||||
@@ -253,18 +253,13 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
||||
}
|
||||
}
|
||||
|
||||
private fun openBiometricPrompt(biometricPrompt: BiometricPrompt?,
|
||||
cryptoObject: BiometricPrompt.CryptoObject?,
|
||||
promptInfo: BiometricPrompt.PromptInfo) {
|
||||
private fun openAdvancedUnlockPrompt(cryptoPrompt: AdvancedUnlockCryptoPrompt) {
|
||||
context.runOnUiThread {
|
||||
if (allowOpenBiometricPrompt) {
|
||||
if (biometricPrompt != null) {
|
||||
if (cryptoObject != null) {
|
||||
biometricPrompt.authenticate(promptInfo, cryptoObject)
|
||||
} else {
|
||||
setAdvancedUnlockedTitleView(R.string.crypto_object_not_initialized)
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
biometricUnlockDatabaseHelper?.openAdvancedUnlockPrompt(cryptoPrompt)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to open advanced unlock prompt", e)
|
||||
setAdvancedUnlockedTitleView(R.string.advanced_unlock_prompt_not_initialized)
|
||||
}
|
||||
}
|
||||
@@ -276,10 +271,10 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
||||
setAdvancedUnlockedTitleView(R.string.open_advanced_unlock_prompt_store_credential)
|
||||
setAdvancedUnlockedMessageView("")
|
||||
|
||||
biometricUnlockDatabaseHelper?.initEncryptData { biometricPrompt, cryptoObject, promptInfo ->
|
||||
biometricUnlockDatabaseHelper?.initEncryptData { cryptoPrompt ->
|
||||
// Set listener to open the biometric dialog and save credential
|
||||
advancedUnlockInfoView?.setIconViewClickListener { _ ->
|
||||
openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
|
||||
openAdvancedUnlockPrompt(cryptoPrompt)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -292,17 +287,17 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
||||
if (biometricUnlockDatabaseHelper != null) {
|
||||
cipherDatabaseAction.getCipherDatabase(databaseFileUri) { cipherDatabase ->
|
||||
cipherDatabase?.let {
|
||||
biometricUnlockDatabaseHelper?.initDecryptData(it.specParameters) { biometricPrompt, cryptoObject, promptInfo ->
|
||||
biometricUnlockDatabaseHelper?.initDecryptData(it.specParameters) { cryptoPrompt ->
|
||||
|
||||
// Set listener to open the biometric dialog and check credential
|
||||
advancedUnlockInfoView?.setIconViewClickListener { _ ->
|
||||
openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
|
||||
openAdvancedUnlockPrompt(cryptoPrompt)
|
||||
}
|
||||
|
||||
// Auto open the biometric prompt
|
||||
if (isBiometricPromptAutoOpenEnable) {
|
||||
isBiometricPromptAutoOpenEnable = false
|
||||
openBiometricPrompt(biometricPrompt, cryptoObject, promptInfo)
|
||||
openAdvancedUnlockPrompt(cryptoPrompt)
|
||||
}
|
||||
}
|
||||
} ?: deleteEncryptedDatabaseKey()
|
||||
@@ -417,6 +412,6 @@ class AdvancedUnlockedManager(var context: FragmentActivity,
|
||||
|
||||
companion object {
|
||||
|
||||
private val TAG = AdvancedUnlockedManager::class.java.name
|
||||
private val TAG = AdvancedUnlockManager::class.java.name
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ import androidx.annotation.RequiresApi
|
||||
import androidx.biometric.BiometricManager
|
||||
import androidx.biometric.BiometricManager.Authenticators.*
|
||||
import androidx.biometric.BiometricPrompt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.kunzisoft.keepass.R
|
||||
import com.kunzisoft.keepass.settings.PreferencesUtil
|
||||
@@ -46,19 +47,18 @@ import javax.crypto.spec.IvParameterSpec
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
|
||||
private var biometricPrompt: BiometricPrompt? = null
|
||||
|
||||
private var keyStore: KeyStore? = null
|
||||
private var keyGenerator: KeyGenerator? = null
|
||||
private var cipher: Cipher? = null
|
||||
private var keyguardManager: KeyguardManager? = null
|
||||
private var cryptoObject: BiometricPrompt.CryptoObject? = null
|
||||
|
||||
private var isKeyManagerInit = false
|
||||
private var biometricPrompt: BiometricPrompt? = null
|
||||
var authenticationCallback: BiometricPrompt.AuthenticationCallback? = null
|
||||
var biometricUnlockCallback: BiometricUnlockCallback? = null
|
||||
|
||||
private var isKeyManagerInit = false
|
||||
|
||||
private val deviceCredentialUnlockEnable = PreferencesUtil.isDeviceCredentialUnlockEnable(context)
|
||||
private val biometricUnlockEnable = PreferencesUtil.isBiometricUnlockEnable(context)
|
||||
|
||||
val isKeyManagerInitialized: Boolean
|
||||
get() {
|
||||
@@ -68,17 +68,31 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
return isKeyManagerInit
|
||||
}
|
||||
|
||||
// Since Android 30, device credential is also a biometric operation
|
||||
private fun isDeviceCredentialOperation(): Boolean {
|
||||
return Build.VERSION.SDK_INT < Build.VERSION_CODES.R
|
||||
&& deviceCredentialUnlockEnable
|
||||
}
|
||||
|
||||
private fun isDeviceCredentialBiometricOperation(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& deviceCredentialUnlockEnable
|
||||
}
|
||||
|
||||
private fun isBiometricOperation(): Boolean {
|
||||
return biometricUnlockEnable || isDeviceCredentialBiometricOperation()
|
||||
}
|
||||
|
||||
init {
|
||||
if (allowInitKeyStore(context)) {
|
||||
this.keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager?
|
||||
if (isDeviceSecure(context)
|
||||
&& (deviceCredentialUnlockEnable || biometricUnlockEnable)) {
|
||||
try {
|
||||
this.keyStore = KeyStore.getInstance(BIOMETRIC_KEYSTORE)
|
||||
this.keyGenerator = KeyGenerator.getInstance(BIOMETRIC_KEY_ALGORITHM, BIOMETRIC_KEYSTORE)
|
||||
this.keyStore = KeyStore.getInstance(ADVANCED_UNLOCK_KEYSTORE)
|
||||
this.keyGenerator = KeyGenerator.getInstance(ADVANCED_UNLOCK_KEY_ALGORITHM, ADVANCED_UNLOCK_KEYSTORE)
|
||||
this.cipher = Cipher.getInstance(
|
||||
BIOMETRIC_KEY_ALGORITHM + "/"
|
||||
+ BIOMETRIC_BLOCKS_MODES + "/"
|
||||
+ BIOMETRIC_ENCRYPTION_PADDING)
|
||||
this.cryptoObject = BiometricPrompt.CryptoObject(cipher!!)
|
||||
ADVANCED_UNLOCK_KEY_ALGORITHM + "/"
|
||||
+ ADVANCED_UNLOCK_BLOCKS_MODES + "/"
|
||||
+ ADVANCED_UNLOCK_ENCRYPTION_PADDING)
|
||||
isKeyManagerInit = (keyStore != null
|
||||
&& keyGenerator != null
|
||||
&& cipher != null)
|
||||
@@ -103,12 +117,12 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
keyStore.load(null)
|
||||
|
||||
try {
|
||||
if (!keyStore.containsAlias(BIOMETRIC_KEYSTORE_KEY)) {
|
||||
if (!keyStore.containsAlias(ADVANCED_UNLOCK_KEYSTORE_KEY)) {
|
||||
// Set the alias of the entry in Android KeyStore where the key will appear
|
||||
// and the constrains (purposes) in the constructor of the Builder
|
||||
keyGenerator?.init(
|
||||
KeyGenParameterSpec.Builder(
|
||||
BIOMETRIC_KEYSTORE_KEY,
|
||||
ADVANCED_UNLOCK_KEYSTORE_KEY,
|
||||
KeyProperties.PURPOSE_ENCRYPT or KeyProperties.PURPOSE_DECRYPT)
|
||||
.setBlockModes(KeyProperties.BLOCK_MODE_CBC)
|
||||
.setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
|
||||
@@ -116,8 +130,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
// of the key
|
||||
.setUserAuthenticationRequired(true)
|
||||
.apply {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
&& deviceCredentialUnlockEnable) {
|
||||
if (isDeviceCredentialBiometricOperation()) {
|
||||
setUserAuthenticationParameters(0, KeyProperties.AUTH_DEVICE_CREDENTIAL)
|
||||
}
|
||||
}
|
||||
@@ -129,7 +142,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
biometricUnlockCallback?.onBiometricException(e)
|
||||
}
|
||||
|
||||
return keyStore.getKey(BIOMETRIC_KEYSTORE_KEY, null) as SecretKey?
|
||||
return keyStore.getKey(ADVANCED_UNLOCK_KEYSTORE_KEY, null) as SecretKey?
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to retrieve the key in keystore", e)
|
||||
@@ -139,33 +152,24 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
}
|
||||
|
||||
fun initEncryptData(actionIfCypherInit
|
||||
: (biometricPrompt: BiometricPrompt?,
|
||||
cryptoObject: BiometricPrompt.CryptoObject?,
|
||||
promptInfo: BiometricPrompt.PromptInfo) -> Unit) {
|
||||
: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit) {
|
||||
if (!isKeyManagerInitialized) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
// TODO if (keyguardManager?.isDeviceSecure == true) {
|
||||
getSecretKey()?.let { secretKey ->
|
||||
cipher?.init(Cipher.ENCRYPT_MODE, secretKey)
|
||||
cipher?.let { cipher ->
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey)
|
||||
|
||||
initBiometricPrompt()
|
||||
|
||||
val promptInfoStoreCredential = BiometricPrompt.PromptInfo.Builder().apply {
|
||||
setTitle(context.getString(R.string.advanced_unlock_prompt_store_credential_title))
|
||||
setDescription(context.getString(R.string.advanced_unlock_prompt_store_credential_message))
|
||||
setConfirmationRequired(true)
|
||||
if (deviceCredentialUnlockEnable) {
|
||||
setAllowedAuthenticators(DEVICE_CREDENTIAL)
|
||||
} else {
|
||||
setNegativeButtonText(context.getString(android.R.string.cancel))
|
||||
}
|
||||
}.build()
|
||||
|
||||
actionIfCypherInit.invoke(biometricPrompt,
|
||||
cryptoObject,
|
||||
promptInfoStoreCredential)
|
||||
actionIfCypherInit.invoke(
|
||||
AdvancedUnlockCryptoPrompt(
|
||||
cipher,
|
||||
R.string.advanced_unlock_prompt_store_credential_title,
|
||||
R.string.advanced_unlock_prompt_store_credential_message,
|
||||
isDeviceCredentialOperation(), isBiometricOperation())
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (unrecoverableKeyException: UnrecoverableKeyException) {
|
||||
Log.e(TAG, "Unable to initialize encrypt data", unrecoverableKeyException)
|
||||
@@ -200,9 +204,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
}
|
||||
|
||||
fun initDecryptData(ivSpecValue: String, actionIfCypherInit
|
||||
: (biometricPrompt: BiometricPrompt?,
|
||||
cryptoObject: BiometricPrompt.CryptoObject?,
|
||||
promptInfo: BiometricPrompt.PromptInfo) -> Unit) {
|
||||
: (cryptoPrompt: AdvancedUnlockCryptoPrompt) -> Unit) {
|
||||
if (!isKeyManagerInitialized) {
|
||||
return
|
||||
}
|
||||
@@ -213,24 +215,17 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
val spec = IvParameterSpec(iv)
|
||||
|
||||
getSecretKey()?.let { secretKey ->
|
||||
cipher?.init(Cipher.DECRYPT_MODE, secretKey, spec)
|
||||
cipher?.let { cipher ->
|
||||
cipher.init(Cipher.DECRYPT_MODE, secretKey, spec)
|
||||
|
||||
initBiometricPrompt()
|
||||
|
||||
val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder().apply {
|
||||
setTitle(context.getString(R.string.advanced_unlock_prompt_extract_credential_title))
|
||||
//setDescription(context.getString(R.string.biometric_prompt_extract_credential_message))
|
||||
setConfirmationRequired(false)
|
||||
if (deviceCredentialUnlockEnable) {
|
||||
setAllowedAuthenticators(DEVICE_CREDENTIAL)
|
||||
} else {
|
||||
setNegativeButtonText(context.getString(android.R.string.cancel))
|
||||
}
|
||||
}.build()
|
||||
|
||||
actionIfCypherInit.invoke(biometricPrompt,
|
||||
cryptoObject,
|
||||
promptInfoExtractCredential)
|
||||
actionIfCypherInit.invoke(
|
||||
AdvancedUnlockCryptoPrompt(
|
||||
cipher,
|
||||
R.string.advanced_unlock_prompt_extract_credential_title,
|
||||
null,
|
||||
isDeviceCredentialOperation(), isBiometricOperation())
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (unrecoverableKeyException: UnrecoverableKeyException) {
|
||||
Log.e(TAG, "Unable to initialize decrypt data", unrecoverableKeyException)
|
||||
@@ -267,20 +262,52 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
fun deleteKeystoreKey() {
|
||||
try {
|
||||
keyStore?.load(null)
|
||||
keyStore?.deleteEntry(BIOMETRIC_KEYSTORE_KEY)
|
||||
keyStore?.deleteEntry(ADVANCED_UNLOCK_KEYSTORE_KEY)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Unable to delete entry key in keystore", e)
|
||||
biometricUnlockCallback?.onBiometricException(e)
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("DEPRECATION")
|
||||
@Synchronized
|
||||
fun initBiometricPrompt() {
|
||||
fun openAdvancedUnlockPrompt(cryptoPrompt: AdvancedUnlockCryptoPrompt) {
|
||||
// Init advanced unlock prompt
|
||||
if (biometricPrompt == null) {
|
||||
authenticationCallback?.let {
|
||||
biometricPrompt = BiometricPrompt(context, Executors.newSingleThreadExecutor(), it)
|
||||
authenticationCallback?.let { callback ->
|
||||
biometricPrompt = BiometricPrompt(context, Executors.newSingleThreadExecutor(), callback)
|
||||
}
|
||||
}
|
||||
|
||||
val promptTitle = context.getString(cryptoPrompt.promptTitleId)
|
||||
val promptDescription = cryptoPrompt.promptDescriptionId?.let { descriptionId ->
|
||||
context.getString(descriptionId)
|
||||
} ?: ""
|
||||
|
||||
if (cryptoPrompt.isDeviceCredentialOperation) {
|
||||
// TODO open intent keyguard for response
|
||||
val keyGuardManager = ContextCompat.getSystemService(context, KeyguardManager::class.java)
|
||||
context.startActivityForResult(
|
||||
keyGuardManager?.createConfirmDeviceCredentialIntent(promptTitle, promptDescription),
|
||||
REQUEST_DEVICE_CREDENTIAL)
|
||||
}
|
||||
else if (cryptoPrompt.isBiometricOperation) {
|
||||
val promptInfoExtractCredential = BiometricPrompt.PromptInfo.Builder().apply {
|
||||
setTitle(promptTitle)
|
||||
if (promptDescription.isNotEmpty())
|
||||
setDescription(promptDescription)
|
||||
setConfirmationRequired(false)
|
||||
if (isDeviceCredentialBiometricOperation()) {
|
||||
setAllowedAuthenticators(DEVICE_CREDENTIAL)
|
||||
} else {
|
||||
setNegativeButtonText(context.getString(android.R.string.cancel))
|
||||
}
|
||||
}.build()
|
||||
|
||||
biometricPrompt?.authenticate(
|
||||
promptInfoExtractCredential,
|
||||
BiometricPrompt.CryptoObject(cryptoPrompt.cipher))
|
||||
}
|
||||
}
|
||||
|
||||
fun closeBiometricPrompt() {
|
||||
@@ -301,11 +328,13 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
|
||||
private val TAG = BiometricUnlockDatabaseHelper::class.java.name
|
||||
|
||||
private const val BIOMETRIC_KEYSTORE = "AndroidKeyStore"
|
||||
private const val BIOMETRIC_KEYSTORE_KEY = "com.kunzisoft.keepass.biometric.key"
|
||||
private const val BIOMETRIC_KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
|
||||
private const val BIOMETRIC_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC
|
||||
private const val BIOMETRIC_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
|
||||
private const val ADVANCED_UNLOCK_KEYSTORE = "AndroidKeyStore"
|
||||
private const val ADVANCED_UNLOCK_KEYSTORE_KEY = "com.kunzisoft.keepass.biometric.key"
|
||||
private const val ADVANCED_UNLOCK_KEY_ALGORITHM = KeyProperties.KEY_ALGORITHM_AES
|
||||
private const val ADVANCED_UNLOCK_BLOCKS_MODES = KeyProperties.BLOCK_MODE_CBC
|
||||
private const val ADVANCED_UNLOCK_ENCRYPTION_PADDING = KeyProperties.ENCRYPTION_PADDING_PKCS7
|
||||
|
||||
private const val REQUEST_DEVICE_CREDENTIAL = 556
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
fun canAuthenticate(context: Context): Int {
|
||||
@@ -336,12 +365,14 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
fun allowInitKeyStore(context: Context): Boolean {
|
||||
val biometricCanAuthenticate = canAuthenticate(context)
|
||||
return ( biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|
||||
)
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
fun isDeviceSecure(context: Context): Boolean {
|
||||
val keyguardManager = ContextCompat.getSystemService(context, KeyguardManager::class.java)
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
keyguardManager?.isDeviceSecure ?: false
|
||||
} else {
|
||||
keyguardManager?.isKeyguardSecure ?: false
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.M)
|
||||
@@ -365,15 +396,26 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
|
||||
)
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.R)
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
|
||||
fun deviceCredentialUnlockSupported(context: Context): Boolean {
|
||||
val biometricCanAuthenticate = BiometricManager.from(context).canAuthenticate(DEVICE_CREDENTIAL)
|
||||
return (biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
|
||||
)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val biometricCanAuthenticate = BiometricManager.from(context).canAuthenticate(DEVICE_CREDENTIAL)
|
||||
return (biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|
||||
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
|
||||
)
|
||||
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
ContextCompat.getSystemService(context, KeyguardManager::class.java)?.apply {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
isDeviceSecure
|
||||
} else {
|
||||
isKeyguardSecure
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -258,7 +258,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
val deviceCredentialUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
val deviceCredentialUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
BiometricUnlockDatabaseHelper.deviceCredentialUnlockSupported(activity)
|
||||
} else false
|
||||
deviceCredentialUnlockEnablePreference?.apply {
|
||||
@@ -266,7 +266,7 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
|
||||
isChecked = false
|
||||
setOnPreferenceClickListener { preference ->
|
||||
(preference as SwitchPreference).isChecked = false
|
||||
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.R)
|
||||
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.LOLLIPOP)
|
||||
.show(parentFragmentManager, "unavailableFeatureDialog")
|
||||
false
|
||||
}
|
||||
|
||||
@@ -504,7 +504,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Pokuste se uložit sdílené info, když manuálné vybíráte položku</string>
|
||||
<string name="keyboard_save_search_info_title">Uložit sdílené info</string>
|
||||
<string name="notification">Oznámení</string>
|
||||
<string name="crypto_object_not_initialized">Krypto objekt nelze načíst.</string>
|
||||
<string name="biometric_security_update_required">Vyžadována aktualizace biometrického zabezpečení.</string>
|
||||
<string name="configure_biometric">Žádné přihlašovací ani biometrické údaje nejsou registrovány.</string>
|
||||
<string name="warning_empty_recycle_bin">Trvale odstranit všechny položky z koše\?</string>
|
||||
|
||||
@@ -491,7 +491,6 @@
|
||||
<string name="database_data_remove_unlinked_attachments_summary">Fjerner vedhæftede filer indeholdt i databasen, men ikke knyttet til en post</string>
|
||||
<string name="database_data_remove_unlinked_attachments_title">Fjern ikke-sammenkædede data</string>
|
||||
<string name="data">Data</string>
|
||||
<string name="crypto_object_not_initialized">Kryptoobjektet kunne ikke hentes.</string>
|
||||
<string name="biometric_security_update_required">Biometrisk sikkerhedsopdatering påkrævet.</string>
|
||||
<string name="warning_empty_keyfile_explanation">Indholdet af nøglefilen bør aldrig ændres og bør i bedste fald indeholde tilfældigt genererede data.</string>
|
||||
<string name="warning_empty_keyfile">Det anbefales ikke at tilføje en tom nøglefil.</string>
|
||||
|
||||
@@ -508,7 +508,6 @@
|
||||
<string name="keyboard_previous_lock_summary">Automatisches Zurückschalten zur vorherigen Tastatur nach dem Sperren der Datenbank</string>
|
||||
<string name="keyboard_previous_lock_title">Datenbank sperren</string>
|
||||
<string name="notification">Benachrichtigung</string>
|
||||
<string name="crypto_object_not_initialized">Kryptoobjekt kann nicht abgerufen werden.</string>
|
||||
<string name="biometric_security_update_required">Biometrisches Sicherheitsupdate erforderlich.</string>
|
||||
<string name="configure_biometric">Es sind keine biometrischen oder Geräteanmeldeinformationen registriert.</string>
|
||||
<string name="registration_mode">Registrierungsmodus</string>
|
||||
|
||||
@@ -503,7 +503,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Προσπαθήστε να αποθηκεύσετε κοινόχρηστες πληροφορίες όταν κάνετε μια χειροκίνητη επιλογή καταχώρησης</string>
|
||||
<string name="keyboard_save_search_info_title">Αποθήκευση κοινόχρηστων πληροφοριών</string>
|
||||
<string name="notification">Ειδοποίηση</string>
|
||||
<string name="crypto_object_not_initialized">Δεν είναι δυνατή η ανάκτηση κρυπτογραφικού αντικειμένου.</string>
|
||||
<string name="biometric_security_update_required">Απαιτείται ενημέρωση βιομετρικής ασφάλειας.</string>
|
||||
<string name="configure_biometric">Κανένα πιστοποιητικό βιομετρίας ή συσκευής δεν είναι εγγεγραμμένο.</string>
|
||||
<string name="warning_empty_recycle_bin">Να διαγραφούν οριστικά όλοι οι κόμβοι από τον κάδο ανακύκλωσης;</string>
|
||||
|
||||
@@ -513,7 +513,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Essayer d’enregistrer les informations partagées lors de la sélection manuelle d’une entrée</string>
|
||||
<string name="keyboard_save_search_info_title">Enregistrer les infos partagées</string>
|
||||
<string name="notification">Notification</string>
|
||||
<string name="crypto_object_not_initialized">Impossible de récupérer l\'objet crypto.</string>
|
||||
<string name="biometric_security_update_required">Mise à jour de sécurité biométrique requise.</string>
|
||||
<string name="warning_empty_recycle_bin">Supprimer définitivement tous les nœuds de la corbeille \?</string>
|
||||
<string name="registration_mode">Mode enregistrement</string>
|
||||
|
||||
@@ -490,7 +490,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Pokušaj spremiti dijeljene informacije prilikom odabira ručnog unosa</string>
|
||||
<string name="keyboard_save_search_info_title">Spremi dijeljene informacije</string>
|
||||
<string name="warning_empty_recycle_bin">Trajno izbrisati sve čvorove iz smeća\?</string>
|
||||
<string name="crypto_object_not_initialized">Nije moguće dohvatiti kripto objekt.</string>
|
||||
<string name="biometric_security_update_required">Potrebno je aktualizirati biometrijsku zaštitu.</string>
|
||||
<string name="configure_biometric">Ne postoji biometrijski ključ niti podaci za prijavu uređaja.</string>
|
||||
<string name="registration_mode">Modus registracije</string>
|
||||
|
||||
@@ -464,7 +464,6 @@
|
||||
<string name="database_data_remove_unlinked_attachments_summary">Eltávolítja azokat a mellékleteket, melyek az adatbázisban szerepelnek, de nem tartoznak bejegyzéshez</string>
|
||||
<string name="database_data_remove_unlinked_attachments_title">Nem összekapcsolt adatok eltávolítása</string>
|
||||
<string name="data">Adatok</string>
|
||||
<string name="crypto_object_not_initialized">A titkosítási objektum nem kérhető le.</string>
|
||||
<string name="biometric_security_update_required">Biometrikus biztonsági frissítés szükséges.</string>
|
||||
<string name="configure_biometric">Nincs biometrikus vagy eszközazonosító beállítva.</string>
|
||||
<string name="warning_empty_keyfile_explanation">A kulcsfájl tartalmának sosem szabad megváltoznia, és a legjobb esetben véletlenszerűen előállított adatokat kellene tartalmaznia.</string>
|
||||
|
||||
@@ -506,7 +506,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Prova a salvare le informazioni condivise quando effettui una selezione di immissione manuale</string>
|
||||
<string name="keyboard_save_search_info_title">Salva le informazioni condivise</string>
|
||||
<string name="notification">Notifica</string>
|
||||
<string name="crypto_object_not_initialized">Impossibile recuperare l\'oggetto crittografico.</string>
|
||||
<string name="biometric_security_update_required">È necessario un aggiornamento della sicurezza biometrica.</string>
|
||||
<string name="warning_empty_recycle_bin">Eliminare definitivamente tutti i nodi dal cestino\?</string>
|
||||
<string name="registration_mode">Modalità registrazione</string>
|
||||
|
||||
@@ -273,7 +273,6 @@
|
||||
<string name="keystore_not_accessible">キーストアが正しく初期化されていません。</string>
|
||||
<string name="encrypted_value_stored">保存された暗号化済みパスワード</string>
|
||||
<string name="no_credentials_stored">データベースの保存済み認証情報はありません。</string>
|
||||
<string name="crypto_object_not_initialized">crypto オブジェクトを取得できません。</string>
|
||||
<string name="database_history">履歴</string>
|
||||
<string name="menu_appearance_settings">デザイン</string>
|
||||
<string name="biometric">生体認証</string>
|
||||
|
||||
@@ -505,7 +505,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Probeer gedeelde informatie op te slaan bij een handmatige invoerselectie</string>
|
||||
<string name="keyboard_save_search_info_title">Gedeelde info opslaan</string>
|
||||
<string name="notification">Melding</string>
|
||||
<string name="crypto_object_not_initialized">Kan crypto-object niet ophalen.</string>
|
||||
<string name="biometric_security_update_required">Biometrische beveiligingsupdate vereist.</string>
|
||||
<string name="configure_biometric">Geen biometrische gegevens of apparaatgegevens geregistreerd.</string>
|
||||
<string name="warning_empty_recycle_bin">Alles definitief uit de prullenbak verwijderen\?</string>
|
||||
|
||||
@@ -503,7 +503,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Spróbuj zapisać udostępnione informacje podczas ręcznego wybierania pozycji</string>
|
||||
<string name="keyboard_save_search_info_title">Zapisz udostępnione informacje</string>
|
||||
<string name="notification">Powiadomienia</string>
|
||||
<string name="crypto_object_not_initialized">Nie można pobrać obiektu kryptograficznego.</string>
|
||||
<string name="biometric_security_update_required">Wymagana aktualizacja zabezpieczeń biometrycznych.</string>
|
||||
<string name="configure_biometric">Nie zarejestrowano żadnych danych biometrycznych ani danych urządzenia.</string>
|
||||
<string name="warning_empty_recycle_bin">Trwale usunąć wszystkie węzły z kosza\?</string>
|
||||
|
||||
@@ -494,7 +494,6 @@
|
||||
<string name="autofill_save_search_info_title">Сохранять данные поиска</string>
|
||||
<string name="keyboard_save_search_info_summary">Сохранять общую информацию при ручном выборе записи</string>
|
||||
<string name="keyboard_save_search_info_title">Сохранять общие данные</string>
|
||||
<string name="crypto_object_not_initialized">Невозможно получить доступ к зашифрованному объекту.</string>
|
||||
<string name="keyboard_previous_lock_title">Блокировка базы</string>
|
||||
<string name="keyboard_previous_lock_summary">Автоматически переключаться на предыдущую клавиатуру после блокировки базы</string>
|
||||
<string name="show_uuid_summary">Показывать UUID, связанный с записью</string>
|
||||
|
||||
@@ -486,7 +486,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Elle girdi seçimi yaparken paylaşılan bilgileri kaydetmeyi dene</string>
|
||||
<string name="keyboard_save_search_info_title">Paylaşılan bilgileri kaydet</string>
|
||||
<string name="notification">Bildirim</string>
|
||||
<string name="crypto_object_not_initialized">Şifreleme nesnesi alınamıyor.</string>
|
||||
<string name="biometric_security_update_required">Biyometrik güvenlik güncellemesi gerekli.</string>
|
||||
<string name="configure_biometric">Biyometrik bilgiler veya aygıt kimlik doğrulama bilgileri kaydedilmedi.</string>
|
||||
<string name="warning_empty_recycle_bin">Geri dönüşüm kutusundaki tüm düğümler kalıcı olarak silinsin mi\?</string>
|
||||
|
||||
@@ -503,7 +503,6 @@
|
||||
<string name="keyboard_save_search_info_summary">Намагатися зберегти спільні відомості під час вибору запису власноруч</string>
|
||||
<string name="keyboard_save_search_info_title">Збереження спільних відомостей</string>
|
||||
<string name="notification">Сповіщення</string>
|
||||
<string name="crypto_object_not_initialized">Не вдалося отримати крипто-об\'єкт.</string>
|
||||
<string name="biometric_security_update_required">Необхідно оновити біометричний захист.</string>
|
||||
<string name="configure_biometric">Біометричних чи облікових даних пристрою не зареєстровано.</string>
|
||||
<string name="warning_empty_recycle_bin">Видалити всі вузли з кошика остаточно\?</string>
|
||||
|
||||
@@ -503,7 +503,6 @@
|
||||
<string name="keyboard_save_search_info_summary">手动选择条目时,尝试保存共享信息</string>
|
||||
<string name="keyboard_save_search_info_title">保存分享的信息</string>
|
||||
<string name="notification">通知</string>
|
||||
<string name="crypto_object_not_initialized">无法检索加密对象。</string>
|
||||
<string name="biometric_security_update_required">需要生物识别安全更新。</string>
|
||||
<string name="configure_biometric">未登记生物识别或设备凭证。</string>
|
||||
<string name="warning_empty_recycle_bin">从回收站永久删除所有节点?</string>
|
||||
|
||||
@@ -287,7 +287,6 @@
|
||||
<string name="advanced_unlock_scanning_error">Advanced unlock error: %1$s</string>
|
||||
<string name="no_credentials_stored">This database does not have stored credential yet.</string>
|
||||
<string name="advanced_unlock_prompt_not_initialized">Unable to initialize advanced unlock prompt.</string>
|
||||
<string name="crypto_object_not_initialized">Unable to retrieve crypto object.</string>
|
||||
<string name="credential_before_click_advanced_unlock_button">Type in the password, and then click the \"Advanced unlock\" button.</string>
|
||||
<string name="database_history">History</string>
|
||||
<string name="menu_appearance_settings">Appearance</string>
|
||||
|
||||
Reference in New Issue
Block a user