diff --git a/app/src/main/java/com/freshkeeper/screens/profileSettings/viewmodel/ProfileSettingsViewModel.kt b/app/src/main/java/com/freshkeeper/screens/profileSettings/viewmodel/ProfileSettingsViewModel.kt index 603bc03..e1ecdf1 100644 --- a/app/src/main/java/com/freshkeeper/screens/profileSettings/viewmodel/ProfileSettingsViewModel.kt +++ b/app/src/main/java/com/freshkeeper/screens/profileSettings/viewmodel/ProfileSettingsViewModel.kt @@ -17,6 +17,13 @@ import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.asStateFlow import javax.inject.Inject +import java.security.KeyStore +import javax.crypto.Cipher +import javax.crypto.KeyGenerator +import javax.crypto.SecretKey +import android.security.keystore.KeyGenParameterSpec +import android.security.keystore.KeyProperties + @HiltViewModel class ProfileSettingsViewModel @Inject @@ -114,15 +121,38 @@ class ProfileSettingsViewModel activity?.let { val executor = ContextCompat.getMainExecutor(context) + + // Ensure key exists. Safe to call each time. + generateSecretKey() + val cipher = try { + getCipher() + } catch (e: Exception) { + _isBiometricEnabled.value = false + return + } + val biometricPrompt = BiometricPrompt( it, executor, object : BiometricPrompt.AuthenticationCallback() { override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { - launchCatching { - accountService.updateBiometricEnabled(true) - _isBiometricEnabled.value = true + // Use the cipher to prove authentication! + val cryptoObject = result.cryptoObject + val cipher = cryptoObject?.cipher + if (cipher != null) { + try { + // Encrypt some dummy data as a proof of auth; discard result + cipher.doFinal(ByteArray(16)) + launchCatching { + accountService.updateBiometricEnabled(true) + _isBiometricEnabled.value = true + } + } catch (e: Exception) { + _isBiometricEnabled.value = false + } + } else { + _isBiometricEnabled.value = false } } @@ -147,7 +177,10 @@ class ProfileSettingsViewModel .setNegativeButtonText(context.getString(R.string.cancel)) .build() - biometricPrompt.authenticate(promptInfo) + biometricPrompt.authenticate( + BiometricPrompt.CryptoObject(cipher), + promptInfo + ) } ?: run { _isBiometricEnabled.value = false }