Add logging for Biometric errors (#5621)

This commit is contained in:
David Perez 2025-07-30 11:56:29 -05:00 committed by GitHub
parent 1799d0b716
commit 3c0818232f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 16 deletions

View File

@ -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
}

View File

@ -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)
}

View File

@ -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 {