[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.platform.manager.FeatureFlagManager
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
import timber.log.Timber
import kotlin.collections.get
/**
@ -71,6 +72,7 @@ class KdfManagerImpl(
onSuccess = {
authDiskSource.userState = authDiskSource.userState
?.toUserStateJsonKdfUpdatedMinimums()
Timber.d("[Auth] Upgraded user's KDF to minimums")
UpdateKdfMinimumsResult.Success
},
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.repository.model.VaultUnlockData
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.toVaultUnlockResult
import com.x8bit.bitwarden.data.vault.repository.util.update
@ -239,7 +240,10 @@ class VaultLockManagerImpl(
trustedDeviceManager
.trustThisDeviceIfNecessary(userId = userId)
updateKdfIfNeeded(initUserCryptoMethod)
migratePinProtectedUserKeyIfNeeded(userId = userId)
migratePinProtectedUserKeyIfNeeded(
userId = userId,
initUserCryptoMethod = initUserCryptoMethod,
)
setVaultToUnlocked(userId = userId)
} else {
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.
*
* @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
if (authDiskSource.getPinProtectedUserKeyEnvelope(userId) != null) return
val inMemoryOnly = authDiskSource.getPinProtectedUserKey(userId) == null
Timber.d("[Auth] Vault unlocked, method: ${initUserCryptoMethod.logTag}")
vaultSdkSource.enrollPinWithEncryptedPin(userId, encryptedPin)
.onSuccess { enrollPinResponse ->
authDiskSource.storeEncryptedPin(
@ -306,6 +317,11 @@ class VaultLockManagerImpl(
pinProtectedUserKey = null,
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.TotpCodeResult
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.toEncryptedSdkFolder
import com.x8bit.bitwarden.data.vault.repository.util.toSdkAccount
@ -326,7 +327,12 @@ class VaultRepositoryImpl(
)
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 {
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.
*
* 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 existingPinProtectedUserKeyEnvelope = authDiskSource
.getPinProtectedUserKeyEnvelope(
userId = userId,
)
if (existingPinProtectedUserKeyEnvelope != null) return
Timber.d("[Auth] Vault unlocked, method: ${initUserCryptoMethod.logTag}")
vaultSdkSource
.enrollPinWithEncryptedPin(
userId = userId,
@ -544,6 +565,7 @@ class VaultRepositoryImpl(
pinProtectedUserKey = null,
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"
}