Better unlock settings

This commit is contained in:
J-Jamet
2020-11-23 20:47:50 +01:00
parent 75f245c7dc
commit 84bb47aa53
5 changed files with 106 additions and 43 deletions

View File

@@ -90,12 +90,12 @@ class UnavailableFeatureDialogFragment : DialogFragment() {
} }
} }
if (apiName.isEmpty()) { if (apiName.isEmpty()) {
val mapper = arrayOf("ANDROID BASE", "ANDROID BASE 1.1", "CUPCAKE", "DONUT", "ECLAIR", "ECLAIR_0_1", "ECLAIR_MR1", "FROYO", "GINGERBREAD", "GINGERBREAD_MR1", "HONEYCOMB", "HONEYCOMB_MR1", "HONEYCOMB_MR2", "ICE_CREAM_SANDWICH", "ICE_CREAM_SANDWICH_MR1", "JELLY_BEAN", "JELLY_BEAN", "JELLY_BEAN", "KITKAT", "KITKAT", "LOLLIPOOP", "LOLLIPOOP_MR1", "MARSHMALLOW", "NOUGAT", "NOUGAT", "OREO", "OREO") val mapper = arrayOf("ANDROID BASE", "ANDROID BASE 1.1", "CUPCAKE", "DONUT", "ECLAIR", "ECLAIR_0_1", "ECLAIR_MR1", "FROYO", "GINGERBREAD", "GINGERBREAD_MR1", "HONEYCOMB", "HONEYCOMB_MR1", "HONEYCOMB_MR2", "ICE_CREAM_SANDWICH", "ICE_CREAM_SANDWICH_MR1", "JELLY_BEAN", "JELLY_BEAN", "JELLY_BEAN", "KITKAT", "KITKAT", "LOLLIPOOP", "LOLLIPOOP_MR1", "MARSHMALLOW", "NOUGAT", "NOUGAT", "OREO", "OREO", "PIE", "", "")
val index = apiNumber - 1 val index = apiNumber - 1
apiName = if (index < mapper.size) mapper[index] else "UNKNOWN_VERSION" apiName = if (index < mapper.size) mapper[index] else "UNKNOWN_VERSION"
} }
if (version.isEmpty()) { if (version.isEmpty()) {
val versions = arrayOf("1.0", "1.1", "1.5", "1.6", "2.0", "2.0.1", "2.1", "2.2.X", "2.3", "2.3.3", "3.0", "3.1", "3.2.0", "4.0.1", "4.0.3", "4.1.0", "4.2.0", "4.3.0", "4.4", "4.4", "5.0", "5.1", "6.0", "7.0", "7.1", "8.0.0", "8.1.0") val versions = arrayOf("1.0", "1.1", "1.5", "1.6", "2.0", "2.0.1", "2.1", "2.2.X", "2.3", "2.3.3", "3.0", "3.1", "3.2.0", "4.0.1", "4.0.3", "4.1.0", "4.2.0", "4.3.0", "4.4", "4.4", "5.0", "5.1", "6.0", "7.0", "7.1", "8.0.0", "8.1.0", "9", "10", "11")
val index = apiNumber - 1 val index = apiNumber - 1
version = if (index < versions.size) versions[index] else "UNKNOWN_VERSION" version = if (index < versions.size) versions[index] else "UNKNOWN_VERSION"
} }

View File

@@ -307,21 +307,26 @@ 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
@RequiresApi(api = Build.VERSION_CODES.M)
fun canAuthenticate(context: Context): Int { fun canAuthenticate(context: Context): Int {
return try { return try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { BiometricManager.from(context).canAuthenticate(
BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG or DEVICE_CREDENTIAL) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
} else { BIOMETRIC_STRONG or DEVICE_CREDENTIAL
BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG) } else {
} BIOMETRIC_STRONG
}
)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with strong biometric.", e) Log.e(TAG, "Unable to authenticate with strong biometric.", e)
try { try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { BiometricManager.from(context).canAuthenticate(
BiometricManager.from(context).canAuthenticate(BIOMETRIC_WEAK or DEVICE_CREDENTIAL) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
} else { BIOMETRIC_WEAK or DEVICE_CREDENTIAL
BiometricManager.from(context).canAuthenticate(BIOMETRIC_WEAK) } else {
} BIOMETRIC_WEAK
}
)
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG, "Unable to authenticate with weak biometric.", e) Log.e(TAG, "Unable to authenticate with weak biometric.", e)
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
@@ -329,6 +334,7 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
} }
} }
@RequiresApi(api = Build.VERSION_CODES.M)
fun allowInitKeyStore(context: Context): Boolean { fun allowInitKeyStore(context: Context): Boolean {
val biometricCanAuthenticate = canAuthenticate(context) val biometricCanAuthenticate = canAuthenticate(context)
return ( biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS return ( biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
@@ -336,19 +342,42 @@ class BiometricUnlockDatabaseHelper(private val context: FragmentActivity) {
) )
} }
fun unlockSupported(context: Context): Boolean { @RequiresApi(api = Build.VERSION_CODES.M)
val biometricCanAuthenticate = canAuthenticate(context) fun biometricUnlockSupported(context: Context): Boolean {
return ( biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS val biometricCanAuthenticate = 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)
BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE
}
}
return (biometricCanAuthenticate == BiometricManager.BIOMETRIC_SUCCESS
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN || biometricCanAuthenticate == BiometricManager.BIOMETRIC_STATUS_UNKNOWN
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE || biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED || biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED
|| biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED || biometricCanAuthenticate == BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED
) )
}
@RequiresApi(api = Build.VERSION_CODES.R)
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
)
} }
/** /**
* Remove entry key in keystore * Remove entry key in keystore
*/ */
@RequiresApi(api = Build.VERSION_CODES.M)
fun deleteEntryKeyInKeystoreForBiometric(context: FragmentActivity, fun deleteEntryKeyInKeystoreForBiometric(context: FragmentActivity,
biometricCallback: BiometricUnlockErrorCallback) { biometricCallback: BiometricUnlockErrorCallback) {
BiometricUnlockDatabaseHelper(context).apply { BiometricUnlockDatabaseHelper(context).apply {

View File

@@ -30,7 +30,6 @@ import android.view.autofill.AutofillManager
import android.widget.Toast import android.widget.Toast
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.biometric.BiometricManager
import androidx.preference.ListPreference import androidx.preference.ListPreference
import androidx.preference.Preference import androidx.preference.Preference
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
@@ -209,14 +208,15 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
activity?.let { activity -> activity?.let { activity ->
val biometricUnlockEnablePreference: SwitchPreference? = findPreference(getString(R.string.biometric_unlock_enable_key)) val biometricUnlockEnablePreference: SwitchPreference? = findPreference(getString(R.string.biometric_unlock_enable_key))
val deleteKeysFingerprints: Preference? = findPreference(getString(R.string.biometric_delete_all_key_key)) val deviceCredentialUnlockEnablePreference: SwitchPreference? = findPreference(getString(R.string.device_credential_unlock_enable_key))
// < M solve verifyError exception val autoOpenPromptPreference: SwitchPreference? = findPreference(getString(R.string.biometric_auto_open_prompt_key))
val biometricUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val biometricUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
BiometricUnlockDatabaseHelper.unlockSupported(activity) BiometricUnlockDatabaseHelper.biometricUnlockSupported(activity)
} else false } else false
if (!biometricUnlockSupported) { biometricUnlockEnablePreference?.apply {
// False if under Marshmallow // False if under Marshmallow
biometricUnlockEnablePreference?.apply { if (!biometricUnlockSupported) {
isChecked = false isChecked = false
setOnPreferenceClickListener { preference -> setOnPreferenceClickListener { preference ->
(preference as SwitchPreference).isChecked = false (preference as SwitchPreference).isChecked = false
@@ -224,9 +224,48 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
.show(parentFragmentManager, "unavailableFeatureDialog") .show(parentFragmentManager, "unavailableFeatureDialog")
false false
} }
} else {
setOnPreferenceChangeListener { _, newValue ->
val checked = (newValue as Boolean)
autoOpenPromptPreference?.isEnabled = checked
|| deviceCredentialUnlockEnablePreference?.isChecked == true
if (checked)
deviceCredentialUnlockEnablePreference?.isChecked = false
true
}
} }
deleteKeysFingerprints?.isEnabled = false }
} else {
val deviceCredentialUnlockSupported = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
BiometricUnlockDatabaseHelper.deviceCredentialUnlockSupported(activity)
} else false
deviceCredentialUnlockEnablePreference?.apply {
if (!deviceCredentialUnlockSupported) {
isChecked = false
setOnPreferenceClickListener { preference ->
(preference as SwitchPreference).isChecked = false
UnavailableFeatureDialogFragment.getInstance(Build.VERSION_CODES.R)
.show(parentFragmentManager, "unavailableFeatureDialog")
false
}
} else {
setOnPreferenceChangeListener { _, newValue ->
val checked = (newValue as Boolean)
autoOpenPromptPreference?.isEnabled = checked ||
biometricUnlockEnablePreference?.isChecked == true
if (checked)
biometricUnlockEnablePreference?.isChecked = false
true
}
}
}
autoOpenPromptPreference?.isEnabled = biometricUnlockEnablePreference?.isChecked == true
|| deviceCredentialUnlockEnablePreference?.isChecked == true
val deleteKeysFingerprints: Preference? = findPreference(getString(R.string.biometric_delete_all_key_key))
if (biometricUnlockSupported || deviceCredentialUnlockSupported) {
deleteKeysFingerprints?.setOnPreferenceClickListener { deleteKeysFingerprints?.setOnPreferenceClickListener {
context?.let { context -> context?.let { context ->
AlertDialog.Builder(context) AlertDialog.Builder(context)
@@ -260,6 +299,8 @@ class NestedAppSettingsFragment : NestedSettingsFragment() {
} }
false false
} }
} else {
deleteKeysFingerprints?.isEnabled = false
} }
} }

View File

@@ -324,8 +324,8 @@
<string name="biometric_unlock_enable_summary">Lets you scan your biometric to open the database</string> <string name="biometric_unlock_enable_summary">Lets you scan your biometric to open the database</string>
<string name="device_credential_unlock_enable_title">Device credential unlocking</string> <string name="device_credential_unlock_enable_title">Device credential unlocking</string>
<string name="device_credential_unlock_enable_summary">Lets you use your device credential to open the database</string> <string name="device_credential_unlock_enable_summary">Lets you use your device credential to open the database</string>
<string name="biometric_auto_open_prompt_title">Auto-open biometric prompt</string> <string name="biometric_auto_open_prompt_title">Auto-open prompt</string>
<string name="biometric_auto_open_prompt_summary">Automatically ask for biometric if the database is set up to use it</string> <string name="biometric_auto_open_prompt_summary">Automatically request advanced unlock if the database is set up to use it</string>
<string name="biometric_delete_all_key_title">Delete encryption keys</string> <string name="biometric_delete_all_key_title">Delete encryption keys</string>
<string name="biometric_delete_all_key_summary">Delete all encryption keys related to advanced unlock recognition</string> <string name="biometric_delete_all_key_summary">Delete all encryption keys related to advanced unlock recognition</string>
<string name="biometric_delete_all_key_warning">Delete all encryption keys related to advanced unlock recognition?</string> <string name="biometric_delete_all_key_warning">Delete all encryption keys related to advanced unlock recognition?</string>

View File

@@ -18,34 +18,27 @@
along with KeePassDX. If not, see <http://www.gnu.org/licenses/>. along with KeePassDX. If not, see <http://www.gnu.org/licenses/>.
--> -->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"> <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<Preference
android:key="@string/advanced_unlock_explanation_key"
android:icon="@drawable/prefs_info_24dp"
android:summary="@string/advanced_unlock_explanation_summary"/>
<PreferenceCategory <PreferenceCategory
android:title="@string/biometric"> android:title="@string/general">
<Preference
android:key="@string/advanced_unlock_explanation_key"
android:icon="@drawable/prefs_info_24dp"
android:summary="@string/advanced_unlock_explanation_summary"/>
<SwitchPreference <SwitchPreference
android:key="@string/biometric_unlock_enable_key" android:key="@string/biometric_unlock_enable_key"
android:title="@string/biometric_unlock_enable_title" android:title="@string/biometric_unlock_enable_title"
android:summary="@string/biometric_unlock_enable_summary" android:summary="@string/biometric_unlock_enable_summary"
android:defaultValue="@bool/biometric_unlock_enable_default"/> android:defaultValue="@bool/biometric_unlock_enable_default"/>
<SwitchPreference
android:key="@string/biometric_auto_open_prompt_key"
android:title="@string/biometric_auto_open_prompt_title"
android:summary="@string/biometric_auto_open_prompt_summary"
android:dependency="@string/biometric_unlock_enable_key"
android:defaultValue="@bool/biometric_auto_open_prompt_default"/>
</PreferenceCategory>
<PreferenceCategory
android:title="@string/device_credential">
<SwitchPreference <SwitchPreference
android:key="@string/device_credential_unlock_enable_key" android:key="@string/device_credential_unlock_enable_key"
android:title="@string/device_credential_unlock_enable_title" android:title="@string/device_credential_unlock_enable_title"
android:summary="@string/device_credential_unlock_enable_summary" android:summary="@string/device_credential_unlock_enable_summary"
android:defaultValue="@bool/device_credential_unlock_enable_default"/> android:defaultValue="@bool/device_credential_unlock_enable_default"/>
</PreferenceCategory> <SwitchPreference
<PreferenceCategory android:key="@string/biometric_auto_open_prompt_key"
android:title="@string/general"> android:title="@string/biometric_auto_open_prompt_title"
android:summary="@string/biometric_auto_open_prompt_summary"
android:defaultValue="@bool/biometric_auto_open_prompt_default"/>
<Preference <Preference
android:key="@string/biometric_delete_all_key_key" android:key="@string/biometric_delete_all_key_key"
android:title="@string/biometric_delete_all_key_title" android:title="@string/biometric_delete_all_key_title"