From 3c0818232f81d7ac43b525d89bc1e8d048207404 Mon Sep 17 00:00:00 2001 From: David Perez Date: Wed, 30 Jul 2025 11:56:29 -0500 Subject: [PATCH] Add logging for Biometric errors (#5621) --- .../BiometricsEncryptionManagerImpl.kt | 40 +++++++++++++------ .../repository/SettingsRepositoryImpl.kt | 13 ++++-- .../vault/repository/VaultRepositoryImpl.kt | 3 ++ 3 files changed, 40 insertions(+), 16 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManagerImpl.kt index 0bb0615b9b..39e223051d 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/manager/BiometricsEncryptionManagerImpl.kt @@ -7,6 +7,7 @@ import com.bitwarden.annotation.OmitFromCoverage import com.x8bit.bitwarden.BuildConfig import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource +import timber.log.Timber import java.security.InvalidAlgorithmParameterException import java.security.InvalidKeyException import java.security.KeyStore @@ -45,9 +46,11 @@ class BiometricsEncryptionManagerImpl( } val cipher = try { Cipher.getInstance(CIPHER_TRANSFORMATION) - } catch (_: NoSuchAlgorithmException) { + } catch (nsae: NoSuchAlgorithmException) { + Timber.w(nsae, "createCipherOrNull failed to get cipher instance") return null - } catch (_: NoSuchPaddingException) { + } catch (nspe: NoSuchPaddingException) { + Timber.w(nspe, "createCipherOrNull failed to get cipher instance") return null } // Instantiate integrity values. @@ -124,20 +127,25 @@ class BiometricsEncryptionManagerImpl( private fun generateKeyOrNull(userId: String): SecretKey? { val keyGen = try { KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, ENCRYPTION_KEYSTORE_NAME) - } catch (_: NoSuchAlgorithmException) { + } catch (nsae: NoSuchAlgorithmException) { + Timber.w(nsae, "generateKeyOrNull failed to get key generator instance") return null - } catch (_: NoSuchProviderException) { + } catch (nspe: NoSuchProviderException) { + Timber.w(nspe, "generateKeyOrNull failed to get key generator instance") return null - } catch (_: IllegalArgumentException) { + } catch (iae: IllegalArgumentException) { + Timber.w(iae, "generateKeyOrNull failed to get key generator instance") return null } return try { keyGen.init(getKeyGenParameterSpec(userId = userId)) keyGen.generateKey() - } catch (_: InvalidAlgorithmParameterException) { + } catch (iape: InvalidAlgorithmParameterException) { + Timber.w(iape, "generateKeyOrNull failed to initialize and generate key") null - } catch (_: ProviderException) { + } catch (pe: ProviderException) { + Timber.w(pe, "generateKeyOrNull failed to initialize and generate key") null } } @@ -150,14 +158,17 @@ class BiometricsEncryptionManagerImpl( keystore .getKey(encryptionKeyName(userId = userId), null) ?.let { it as SecretKey } - } catch (_: KeyStoreException) { + } catch (kse: KeyStoreException) { // keystore was not loaded + Timber.w(kse, "getSecretKeyOrNull failed to retrieve secret key") null - } catch (_: NoSuchAlgorithmException) { + } catch (nsae: NoSuchAlgorithmException) { // keystore algorithm cannot be found + Timber.w(nsae, "getSecretKeyOrNull failed to retrieve secret key") null - } catch (_: UnrecoverableKeyException) { + } catch (uke: UnrecoverableKeyException) { // key could not be recovered + Timber.w(uke, "getSecretKeyOrNull failed to retrieve secret key") null } @@ -174,16 +185,19 @@ class BiometricsEncryptionManagerImpl( ?.let { init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(it)) } ?: init(Cipher.ENCRYPT_MODE, secretKey) true - } catch (_: KeyPermanentlyInvalidatedException) { + } catch (kpie: KeyPermanentlyInvalidatedException) { // Biometric has changed + Timber.w(kpie, "initializeCipher failed to initialize cipher") destroyBiometrics(userId = userId) false - } catch (_: UnrecoverableKeyException) { + } catch (uke: UnrecoverableKeyException) { // Biometric was disabled and re-enabled + Timber.w(uke, "initializeCipher failed to initialize cipher") destroyBiometrics(userId = userId) false - } catch (_: InvalidKeyException) { + } catch (ike: InvalidKeyException) { // User has no key + Timber.w(ike, "initializeCipher failed to initialize cipher") destroyBiometrics(userId = userId) true } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt index 38b3d85803..8afd705644 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryImpl.kt @@ -35,6 +35,8 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch +import timber.log.Timber +import java.security.GeneralSecurityException import java.time.Instant import javax.crypto.Cipher @@ -501,9 +503,14 @@ class SettingsRepositoryImpl( .onSuccess { biometricsKey -> authDiskSource.storeUserBiometricUnlockKey( userId = userId, - biometricsKey = cipher - .doFinal(biometricsKey.encodeToByteArray()) - .toString(Charsets.ISO_8859_1), + biometricsKey = try { + cipher + .doFinal(biometricsKey.encodeToByteArray()) + .toString(Charsets.ISO_8859_1) + } catch (e: GeneralSecurityException) { + Timber.w(e, "setupBiometricsKey failed encrypt the biometric key") + return BiometricsKeyResult.Error(error = e) + }, ) authDiskSource.storeUserBiometricInitVector(userId = userId, iv = cipher.iv) } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt index 9baa2ce45e..a6474e41ce 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt @@ -121,6 +121,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import retrofit2.HttpException +import timber.log.Timber import java.security.GeneralSecurityException import java.time.Clock import java.time.temporal.ChronoUnit @@ -572,6 +573,7 @@ class VaultRepositoryImpl( .doFinal(biometricsKey.toByteArray(Charsets.ISO_8859_1)) .decodeToString() } catch (e: GeneralSecurityException) { + Timber.w(e, "unlockVaultWithBiometrics failed when decrypting biometrics key") return VaultUnlockResult.BiometricDecodingError(error = e) } } @@ -585,6 +587,7 @@ class VaultRepositoryImpl( .doFinal(biometricsKey.encodeToByteArray()) .toString(Charsets.ISO_8859_1) } catch (e: GeneralSecurityException) { + Timber.w(e, "unlockVaultWithBiometrics failed to migrate the user to IV encryption") return VaultUnlockResult.BiometricDecodingError(error = e) } } else {