[PM-26420] Add flight recorder logs for vault unlock method and PIN migration (#6052)

This commit is contained in:
André Bispo 2025-10-20 23:29:10 +01:00 committed by GitHub
parent 9874aad65a
commit 9f4bd70c8d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 64 additions and 5 deletions

View File

@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.auth.repository.util.toUserStateJsonKdfUpdatedMi
import com.x8bit.bitwarden.data.auth.util.KdfParamsConstants.DEFAULT_PBKDF2_ITERATIONS import com.x8bit.bitwarden.data.auth.util.KdfParamsConstants.DEFAULT_PBKDF2_ITERATIONS
import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager import com.x8bit.bitwarden.data.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import timber.log.Timber
import kotlin.collections.get import kotlin.collections.get
/** /**
@ -71,6 +72,7 @@ class KdfManagerImpl(
onSuccess = { onSuccess = {
authDiskSource.userState = authDiskSource.userState authDiskSource.userState = authDiskSource.userState
?.toUserStateJsonKdfUpdatedMinimums() ?.toUserStateJsonKdfUpdatedMinimums()
Timber.d("[Auth] Upgraded user's KDF to minimums")
UpdateKdfMinimumsResult.Success UpdateKdfMinimumsResult.Success
}, },
onFailure = { UpdateKdfMinimumsResult.Error(error = it) }, onFailure = { UpdateKdfMinimumsResult.Error(error = it) },

View File

@ -39,6 +39,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResul
import com.x8bit.bitwarden.data.vault.manager.model.VaultStateEvent import com.x8bit.bitwarden.data.vault.manager.model.VaultStateEvent
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.logTag
import com.x8bit.bitwarden.data.vault.repository.util.statusFor import com.x8bit.bitwarden.data.vault.repository.util.statusFor
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.update import com.x8bit.bitwarden.data.vault.repository.util.update
@ -239,7 +240,10 @@ class VaultLockManagerImpl(
trustedDeviceManager trustedDeviceManager
.trustThisDeviceIfNecessary(userId = userId) .trustThisDeviceIfNecessary(userId = userId)
updateKdfIfNeeded(initUserCryptoMethod) updateKdfIfNeeded(initUserCryptoMethod)
migratePinProtectedUserKeyIfNeeded(userId = userId) migratePinProtectedUserKeyIfNeeded(
userId = userId,
initUserCryptoMethod = initUserCryptoMethod,
)
setVaultToUnlocked(userId = userId) setVaultToUnlocked(userId = userId)
} else { } else {
incrementInvalidUnlockCount(userId = userId) incrementInvalidUnlockCount(userId = userId)
@ -284,12 +288,19 @@ class VaultLockManagerImpl(
* Optionally marks the envelope as in-memory only if the PIN-protected user key is not present. * Optionally marks the envelope as in-memory only if the PIN-protected user key is not present.
* *
* @param userId The ID of the user for whom to migrate the PIN-protected user key. * @param userId The ID of the user for whom to migrate the PIN-protected user key.
* @param initUserCryptoMethod The method used to initialize the user's crypto.
*/ */
private suspend fun migratePinProtectedUserKeyIfNeeded(userId: String) { private suspend fun migratePinProtectedUserKeyIfNeeded(
userId: String,
initUserCryptoMethod: InitUserCryptoMethod,
) {
val encryptedPin = authDiskSource.getEncryptedPin(userId) ?: return val encryptedPin = authDiskSource.getEncryptedPin(userId) ?: return
if (authDiskSource.getPinProtectedUserKeyEnvelope(userId) != null) return if (authDiskSource.getPinProtectedUserKeyEnvelope(userId) != null) return
val inMemoryOnly = authDiskSource.getPinProtectedUserKey(userId) == null val inMemoryOnly = authDiskSource.getPinProtectedUserKey(userId) == null
Timber.d("[Auth] Vault unlocked, method: ${initUserCryptoMethod.logTag}")
vaultSdkSource.enrollPinWithEncryptedPin(userId, encryptedPin) vaultSdkSource.enrollPinWithEncryptedPin(userId, encryptedPin)
.onSuccess { enrollPinResponse -> .onSuccess { enrollPinResponse ->
authDiskSource.storeEncryptedPin( authDiskSource.storeEncryptedPin(
@ -306,6 +317,11 @@ class VaultLockManagerImpl(
pinProtectedUserKey = null, pinProtectedUserKey = null,
inMemoryOnly = inMemoryOnly, inMemoryOnly = inMemoryOnly,
) )
if (inMemoryOnly) {
Timber.d("[Auth] Set PIN-protected user key in memory")
} else {
Timber.d("[Auth] Migrated from legacy PIN to PIN-protected user key envelope")
}
} }
} }

View File

@ -40,6 +40,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult
import com.x8bit.bitwarden.data.vault.repository.model.ImportCredentialsResult import com.x8bit.bitwarden.data.vault.repository.model.ImportCredentialsResult
import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult
import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.logTag
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolder import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolder
import com.x8bit.bitwarden.data.vault.repository.util.toSdkAccount import com.x8bit.bitwarden.data.vault.repository.util.toSdkAccount
@ -326,7 +327,12 @@ class VaultRepositoryImpl(
) )
authDiskSource.storeUserBiometricInitVector(userId = userId, iv = cipher.iv) authDiskSource.storeUserBiometricInitVector(userId = userId, iv = cipher.iv)
} }
deriveTemporaryPinProtectedUserKeyIfNecessary(userId = userId) deriveTemporaryPinProtectedUserKeyIfNecessary(
userId = userId,
initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey(
decryptedUserKey = decryptedUserKey,
),
)
} }
} }
} }
@ -350,7 +356,13 @@ class VaultRepositoryImpl(
) )
.also { .also {
if (it is VaultUnlockResult.Success) { if (it is VaultUnlockResult.Success) {
deriveTemporaryPinProtectedUserKeyIfNecessary(userId = userId) deriveTemporaryPinProtectedUserKeyIfNecessary(
userId = userId,
initUserCryptoMethod = InitUserCryptoMethod.Password(
password = masterPassword,
userKey = userKey,
),
)
} }
} }
} }
@ -516,14 +528,23 @@ class VaultRepositoryImpl(
* unlocks during this current app session. * unlocks during this current app session.
* *
* If the user's vault has not yet been unlocked, this call will do nothing. * If the user's vault has not yet been unlocked, this call will do nothing.
*
* @param userId The ID of the user to check.
* @param initUserCryptoMethod The method used to initialize the user's crypto.
*/ */
private suspend fun deriveTemporaryPinProtectedUserKeyIfNecessary(userId: String) { private suspend fun deriveTemporaryPinProtectedUserKeyIfNecessary(
userId: String,
initUserCryptoMethod: InitUserCryptoMethod,
) {
val encryptedPin = authDiskSource.getEncryptedPin(userId = userId) ?: return val encryptedPin = authDiskSource.getEncryptedPin(userId = userId) ?: return
val existingPinProtectedUserKeyEnvelope = authDiskSource val existingPinProtectedUserKeyEnvelope = authDiskSource
.getPinProtectedUserKeyEnvelope( .getPinProtectedUserKeyEnvelope(
userId = userId, userId = userId,
) )
if (existingPinProtectedUserKeyEnvelope != null) return if (existingPinProtectedUserKeyEnvelope != null) return
Timber.d("[Auth] Vault unlocked, method: ${initUserCryptoMethod.logTag}")
vaultSdkSource vaultSdkSource
.enrollPinWithEncryptedPin( .enrollPinWithEncryptedPin(
userId = userId, userId = userId,
@ -544,6 +565,7 @@ class VaultRepositoryImpl(
pinProtectedUserKey = null, pinProtectedUserKey = null,
inMemoryOnly = true, inMemoryOnly = true,
) )
Timber.d("[Auth] Set PIN-protected user key in memory")
} }
} }

View File

@ -0,0 +1,19 @@
package com.x8bit.bitwarden.data.vault.repository.util
import com.bitwarden.core.InitUserCryptoMethod
/**
* Returns the label for the given [InitUserCryptoMethod].
* This will be only used for logging purposes, therefore it is not localized.
*/
val InitUserCryptoMethod.logTag: String
get() = when (this) {
is InitUserCryptoMethod.AuthRequest -> "Auth Request"
is InitUserCryptoMethod.DecryptedKey -> "Decrypted Key (Never Lock/Biometrics)"
is InitUserCryptoMethod.DeviceKey -> "Device Key"
is InitUserCryptoMethod.KeyConnector -> "Key Connector"
is InitUserCryptoMethod.Password -> "Password"
is InitUserCryptoMethod.Pin -> "Pin"
is InitUserCryptoMethod.PinEnvelope -> "Pin Envelope"
is InitUserCryptoMethod.MasterPasswordUnlock -> "Master Password Unlock"
}