From a8432d48a4367cea76dad1deeaefaef9e03c0ace Mon Sep 17 00:00:00 2001 From: Max Schwinghammer Date: Wed, 29 Oct 2025 14:27:50 +0100 Subject: [PATCH] Potential fix for code scanning alert no. 8: Insecure local authentication Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .../viewmodel/ProfileSettingsViewModel.kt | 41 +++++++++++++++++-- 1 file changed, 37 insertions(+), 4 deletions(-) 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 }