diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt index b1ef0d0522..5c1df0475d 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSource.kt @@ -199,16 +199,11 @@ interface AuthDiskSource : AppIdProvider { * Retrieves a pin-protected user key for the given [userId]. */ @Deprecated( - message = "Use getPinProtectedUserKeyEnvelope instead.", - replaceWith = ReplaceWith("getPinProtectedUserKeyEnvelope"), + message = "Use getEphemeralPinProtectedUserKeyEnvelope and " + + "getPersistentPinProtectedUserKeyEnvelope instead.", ) fun getPinProtectedUserKey(userId: String): String? - /** - * Retrieves a pin-protected user key envelope for the given [userId]. - */ - fun getPinProtectedUserKeyEnvelope(userId: String): String? - /** * Stores a pin-protected user key for the given [userId]. * @@ -216,8 +211,8 @@ interface AuthDiskSource : AppIdProvider { * [getPinProtectedUserKey] during the current app session. */ @Deprecated( - message = "Use storePinProtectedUserKeyEnvelope instead.", - replaceWith = ReplaceWith("storePinProtectedUserKeyEnvelope"), + message = "Use storeEphemeralPinProtectedUserKeyEnvelope and " + + "storePersistentPinProtectedUserKeyEnvelope instead.", ) fun storePinProtectedUserKey( userId: String, @@ -225,31 +220,62 @@ interface AuthDiskSource : AppIdProvider { inMemoryOnly: Boolean = false, ) - /** - * Stores a pin-protected user key envelope for the given [userId]. - * - * When [inMemoryOnly] is `true`, the value will only be available via a call to - * [getPinProtectedUserKeyEnvelope] during the current app session. - */ - fun storePinProtectedUserKeyEnvelope( - userId: String, - pinProtectedUserKeyEnvelope: String?, - inMemoryOnly: Boolean = false, - ) - /** * Retrieves a flow for the pin-protected user key for the given [userId]. */ @Deprecated( - message = "Use getPinProtectedUserKeyEnvelopeFlow instead.", - replaceWith = ReplaceWith("getPinProtectedUserKeyEnvelopeFlow"), + message = "Use getEphemeralPinProtectedUserKeyEnvelopeFlow and " + + "getPersistentPinProtectedUserKeyEnvelopeFlow instead.", ) fun getPinProtectedUserKeyFlow(userId: String): Flow /** - * Retrieves a flow for the pin-protected user key envelope for the given [userId]. + * Retrieves the pin-protected user key envelope for the given [userId]. + * + * This will return either the in-memory or persistent pin-protected user key, whichever is + * available. */ - fun getPinProtectedUserKeyEnvelopeFlow(userId: String): Flow + fun getPinProtectedUserKeyEnvelope(userId: String): String? + + /** + * Retrieves the ephemeral pin-protected user key envelope for the given [userId]. + * + * This will always ignore the persistent pin-protected user key. + */ + fun getEphemeralPinProtectedUserKeyEnvelope(userId: String): String? + + /** + * Retrieves the persistent pin-protected user key envelope for the given [userId]. + * + * This will always ignore the ephemeral pin-protected user key. + */ + fun getPersistentPinProtectedUserKeyEnvelope(userId: String): String? + + /** + * Stores an ephemeral pin-protected user key envelope for the given [userId]. + */ + fun storeEphemeralPinProtectedUserKeyEnvelope( + userId: String, + pinProtectedUserKeyEnvelope: String?, + ) + + /** + * Stores a persistent pin-protected user key envelope for the given [userId]. + */ + fun storePersistentPinProtectedUserKeyEnvelope( + userId: String, + pinProtectedUserKeyEnvelope: String?, + ) + + /** + * Retrieves a flow for the ephemeral pin-protected user key envelope for the given [userId]. + */ + fun getEphemeralPinProtectedUserKeyEnvelopeFlow(userId: String): Flow + + /** + * Retrieves a flow for the persistent pin-protected user key envelope for the given [userId]. + */ + fun getPersistentPinProtectedUserKeyEnvelopeFlow(userId: String): Flow /** * Gets a two-factor auth token using a user's [email]. diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt index dbaa71ab24..07033fd4fd 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceImpl.kt @@ -74,8 +74,11 @@ class AuthDiskSourceImpl( ), AuthDiskSource { - private val inMemoryPinProtectedUserKeys = mutableMapOf() - private val inMemoryPinProtectedUserKeyEnvelopes = mutableMapOf() + // Ephemeral in-memory values + private val ephemeralPinProtectedUserKeys = mutableMapOf() + private val ephemeralPinProtectedUserKeyEnvelopes = mutableMapOf() + + // Shared flows for observing data changes private val mutableShouldUseKeyConnectorFlowMap = mutableMapOf>() private val mutableOrganizationsFlowMap = @@ -91,7 +94,9 @@ class AuthDiskSourceImpl( mutableMapOf>() private val mutablePinProtectedUserKeyFlowMap = mutableMapOf>() - private val mutablePinProtectedUserKeyEnvelopeFlowMap = + private val mutableEphemeralPinProtectedUserKeyEnvelopeFlowMap = + mutableMapOf>() + private val mutablePersistentPinProtectedUserKeyEnvelopeFlowMap = mutableMapOf>() private val mutableUserStateFlow = bufferedMutableSharedFlow(replay = 1) @@ -179,7 +184,14 @@ class AuthDiskSourceImpl( storeLastLockTimestamp(userId = userId, lastLockTimestamp = null) storeEncryptedPin(userId = userId, encryptedPin = null) storePinProtectedUserKey(userId = userId, pinProtectedUserKey = null) - storePinProtectedUserKeyEnvelope(userId = userId, pinProtectedUserKeyEnvelope = null) + storeEphemeralPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = null, + ) + storePersistentPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = null, + ) // Certain values are never removed as required by the feature requirements: // * DeviceKey @@ -342,29 +354,23 @@ class AuthDiskSourceImpl( .onSubscription { emit(getUserBiometricUnlockKey(userId = userId)) } @Deprecated( - "Use getPinProtectedUserKeyEnvelope instead.", - replaceWith = ReplaceWith("getPinProtectedUserKeyEnvelope"), + "Use getEphemeralPinProtectedUserKeyEnvelope and " + + "getPersistentPinProtectedUserKeyEnvelope instead.", ) override fun getPinProtectedUserKey(userId: String): String? = - inMemoryPinProtectedUserKeys[userId] + ephemeralPinProtectedUserKeys[userId] ?: getString(key = PIN_PROTECTED_USER_KEY_KEY.appendIdentifier(userId)) - override fun getPinProtectedUserKeyEnvelope(userId: String): String? = - inMemoryPinProtectedUserKeyEnvelopes[userId] - ?: getString( - key = PIN_PROTECTED_USER_KEY_KEY_ENVELOPE.appendIdentifier(userId), - ) - @Deprecated( - "Use storePinProtectedUserKeyEnvelope instead.", - replaceWith = ReplaceWith("storePinProtectedUserKeyEnvelope"), + "Use storeEphemeralPinProtectedUserKeyEnvelope and " + + "storePersistentPinProtectedUserKeyEnvelope instead.", ) override fun storePinProtectedUserKey( userId: String, pinProtectedUserKey: String?, inMemoryOnly: Boolean, ) { - inMemoryPinProtectedUserKeys[userId] = pinProtectedUserKey + ephemeralPinProtectedUserKeys[userId] = pinProtectedUserKey if (inMemoryOnly) return putString( key = PIN_PROTECTED_USER_KEY_KEY.appendIdentifier(userId), @@ -373,34 +379,57 @@ class AuthDiskSourceImpl( getMutablePinProtectedUserKeyFlow(userId).tryEmit(pinProtectedUserKey) } - override fun storePinProtectedUserKeyEnvelope( - userId: String, - pinProtectedUserKeyEnvelope: String?, - inMemoryOnly: Boolean, - ) { - inMemoryPinProtectedUserKeyEnvelopes[userId] = pinProtectedUserKeyEnvelope - if (inMemoryOnly) { - getMutablePinProtectedUserKeyEnvelopeFlow(userId).tryEmit(pinProtectedUserKeyEnvelope) - return - } - putString( - key = PIN_PROTECTED_USER_KEY_KEY_ENVELOPE.appendIdentifier(userId), - value = pinProtectedUserKeyEnvelope, - ) - getMutablePinProtectedUserKeyEnvelopeFlow(userId).tryEmit(pinProtectedUserKeyEnvelope) - } - @Deprecated( - "Use getPinProtectedUserKeyEnvelopeFlow instead.", - replaceWith = ReplaceWith("getPinProtectedUserKeyEnvelopeFlow"), + "Use getEphemeralPinProtectedUserKeyEnvelopeFlow and " + + "getPersistentPinProtectedUserKeyEnvelopeFlow instead.", ) override fun getPinProtectedUserKeyFlow(userId: String): Flow = getMutablePinProtectedUserKeyFlow(userId) .onSubscription { emit(getPinProtectedUserKey(userId = userId)) } - override fun getPinProtectedUserKeyEnvelopeFlow(userId: String): Flow = - getMutablePinProtectedUserKeyEnvelopeFlow(userId) - .onSubscription { emit(getPinProtectedUserKeyEnvelope(userId = userId)) } + override fun getPinProtectedUserKeyEnvelope( + userId: String, + ): String? = getEphemeralPinProtectedUserKeyEnvelope(userId = userId) + ?: getPersistentPinProtectedUserKeyEnvelope(userId = userId) + + override fun getEphemeralPinProtectedUserKeyEnvelope( + userId: String, + ): String? = ephemeralPinProtectedUserKeyEnvelopes[userId] + + override fun getPersistentPinProtectedUserKeyEnvelope( + userId: String, + ): String? = getString(key = PIN_PROTECTED_USER_KEY_KEY_ENVELOPE.appendIdentifier(userId)) + + override fun storeEphemeralPinProtectedUserKeyEnvelope( + userId: String, + pinProtectedUserKeyEnvelope: String?, + ) { + ephemeralPinProtectedUserKeyEnvelopes[userId] = pinProtectedUserKeyEnvelope + getMutableEphemeralPinProtectedUserKeyEnvelopeFlow(userId = userId) + .tryEmit(pinProtectedUserKeyEnvelope) + } + + override fun storePersistentPinProtectedUserKeyEnvelope( + userId: String, + pinProtectedUserKeyEnvelope: String?, + ) { + putString( + key = PIN_PROTECTED_USER_KEY_KEY_ENVELOPE.appendIdentifier(userId), + value = pinProtectedUserKeyEnvelope, + ) + getMutablePersistentPinProtectedUserKeyEnvelopeFlow(userId = userId) + .tryEmit(pinProtectedUserKeyEnvelope) + } + + override fun getEphemeralPinProtectedUserKeyEnvelopeFlow( + userId: String, + ): Flow = getMutableEphemeralPinProtectedUserKeyEnvelopeFlow(userId = userId) + .onSubscription { emit(getEphemeralPinProtectedUserKeyEnvelope(userId = userId)) } + + override fun getPersistentPinProtectedUserKeyEnvelopeFlow( + userId: String, + ): Flow = getMutablePersistentPinProtectedUserKeyEnvelopeFlow(userId = userId) + .onSubscription { emit(getPersistentPinProtectedUserKeyEnvelope(userId = userId)) } override fun getTwoFactorToken(email: String): String? = getString(key = TWO_FACTOR_TOKEN_KEY.appendIdentifier(email)) @@ -635,11 +664,17 @@ class AuthDiskSourceImpl( bufferedMutableSharedFlow(replay = 1) } - private fun getMutablePinProtectedUserKeyEnvelopeFlow( + private fun getMutableEphemeralPinProtectedUserKeyEnvelopeFlow( userId: String, - ): MutableSharedFlow = mutablePinProtectedUserKeyEnvelopeFlowMap.getOrPut(userId) { - bufferedMutableSharedFlow(replay = 1) - } + ): MutableSharedFlow = mutableEphemeralPinProtectedUserKeyEnvelopeFlowMap.getOrPut( + key = userId, + ) { bufferedMutableSharedFlow(replay = 1) } + + private fun getMutablePersistentPinProtectedUserKeyEnvelopeFlow( + userId: String, + ): MutableSharedFlow = mutablePersistentPinProtectedUserKeyEnvelopeFlowMap.getOrPut( + key = userId, + ) { bufferedMutableSharedFlow(replay = 1) } private fun migrateAccountTokens() { userState diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerImpl.kt index 5f40279a27..4ab9474939 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerImpl.kt @@ -82,9 +82,10 @@ class UserLogoutManagerImpl( val vaultTimeoutAction = settingsDiskSource.getVaultTimeoutAction(userId = userId) val encryptedPin = authDiskSource.getEncryptedPin(userId = userId) val pinProtectedUserKey = authDiskSource.getPinProtectedUserKey(userId = userId) - val pinProtectedUserKeyEnvelope = authDiskSource.getPinProtectedUserKeyEnvelope( - userId = userId, - ) + val ephemeralPinProtectedUserKeyEnvelope = authDiskSource + .getEphemeralPinProtectedUserKeyEnvelope(userId = userId) + val persistentPinProtectedUserKeyEnvelope = authDiskSource + .getPersistentPinProtectedUserKeyEnvelope(userId = userId) clearData(userId = userId) mutableLogoutEventFlow.tryEmit(LogoutEvent(loggedOutUserId = userId)) @@ -103,9 +104,13 @@ class UserLogoutManagerImpl( authDiskSource.apply { storeEncryptedPin(userId = userId, encryptedPin = encryptedPin) storePinProtectedUserKey(userId = userId, pinProtectedUserKey = pinProtectedUserKey) - storePinProtectedUserKeyEnvelope( + storeEphemeralPinProtectedUserKeyEnvelope( userId = userId, - pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, + pinProtectedUserKeyEnvelope = ephemeralPinProtectedUserKeyEnvelope, + ) + storePersistentPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = persistentPinProtectedUserKeyEnvelope, ) } } 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 a709865e28..e0389f66df 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 @@ -294,13 +294,15 @@ class SettingsRepositoryImpl( override val isUnlockWithPinEnabledFlow: Flow get() = activeUserId ?.let { userId -> - authDiskSource - .getPinProtectedUserKeyFlow(userId) - .combine( - authDiskSource.getPinProtectedUserKeyEnvelopeFlow(userId), - ) { pinProtectedUserKey, pinProtectedUserKeyEnvelope -> - pinProtectedUserKey != null || pinProtectedUserKeyEnvelope != null - } + combine( + authDiskSource.getPinProtectedUserKeyFlow(userId = userId), + authDiskSource.getEphemeralPinProtectedUserKeyEnvelopeFlow(userId = userId), + authDiskSource.getPersistentPinProtectedUserKeyEnvelopeFlow(userId = userId), + ) { pinUserKey, ephemeralPinUserKeyEnvelope, persistentPinUserKeyEnvelope -> + pinUserKey != null || + ephemeralPinUserKeyEnvelope != null || + persistentPinUserKeyEnvelope != null + } } ?: flowOf(false) @@ -627,17 +629,21 @@ class SettingsRepositoryImpl( userId = userId, encryptedPin = enrollPinResponse.userKeyEncryptedPin, ) - storePinProtectedUserKeyEnvelope( - userId = userId, - pinProtectedUserKeyEnvelope = - enrollPinResponse.pinProtectedUserKeyEnvelope, - inMemoryOnly = shouldRequireMasterPasswordOnRestart, - ) + if (shouldRequireMasterPasswordOnRestart) { + storeEphemeralPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = enrollPinResponse + .pinProtectedUserKeyEnvelope, + ) + } else { + storePersistentPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = enrollPinResponse + .pinProtectedUserKeyEnvelope, + ) + } // Remove any legacy pin protected user keys. - storePinProtectedUserKey( - userId = userId, - pinProtectedUserKey = null, - ) + storePinProtectedUserKey(userId = userId, pinProtectedUserKey = null) } }, onFailure = { @@ -652,18 +658,16 @@ class SettingsRepositoryImpl( override fun clearUnlockPin() { val userId = activeUserId ?: return authDiskSource.apply { - storeEncryptedPin( - userId = userId, - encryptedPin = null, - ) - authDiskSource.storePinProtectedUserKeyEnvelope( + storeEncryptedPin(userId = userId, encryptedPin = null) + storeEphemeralPinProtectedUserKeyEnvelope( userId = userId, pinProtectedUserKeyEnvelope = null, ) - authDiskSource.storePinProtectedUserKey( + storePersistentPinProtectedUserKeyEnvelope( userId = userId, - pinProtectedUserKey = null, + pinProtectedUserKeyEnvelope = null, ) + storePinProtectedUserKey(userId = userId, pinProtectedUserKey = null) } } diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/PinProtectedUserKeyManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/PinProtectedUserKeyManagerImpl.kt index f280cdd533..e19ec1c483 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/PinProtectedUserKeyManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/PinProtectedUserKeyManagerImpl.kt @@ -67,7 +67,7 @@ internal class PinProtectedUserKeyManagerImpl( userId = userId, encryptedPin = null, pinProtectedUserKeyEnvelope = null, - inMemoryOnly = false, + inMemoryOnly = inMemoryOnly, ) } @@ -78,11 +78,17 @@ internal class PinProtectedUserKeyManagerImpl( inMemoryOnly: Boolean, ) { authDiskSource.storeEncryptedPin(userId = userId, encryptedPin = encryptedPin) - authDiskSource.storePinProtectedUserKeyEnvelope( - userId = userId, - pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, - inMemoryOnly = inMemoryOnly, - ) + if (inMemoryOnly) { + authDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, + ) + } else { + authDiskSource.storePersistentPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, + ) + } // This property is deprecated and we should be migrated to the PinProtectedUserKeyEnvelope. // Because of this, we always clear this value and it should always be cleared at the disk // level, not the in-memory level. diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt index 6133de6d19..912515cf84 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/AuthDiskSourceTest.kt @@ -283,9 +283,13 @@ class AuthDiskSourceTest { userId = userId, pinProtectedUserKey = "pinProtectedUserKey", ) - authDiskSource.storePinProtectedUserKeyEnvelope( + authDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( userId = userId, - pinProtectedUserKeyEnvelope = "pinProtectedUserKeyEnvelope", + pinProtectedUserKeyEnvelope = "ephemeralPinProtectedUserKeyEnvelope", + ) + authDiskSource.storePersistentPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = "persistentPinProtectedUserKeyEnvelope", ) authDiskSource.storeInvalidUnlockAttempts( userId = userId, @@ -358,6 +362,8 @@ class AuthDiskSourceTest { assertNull(authDiskSource.getEncryptedPin(userId = userId)) assertNull(authDiskSource.getPinProtectedUserKey(userId = userId)) assertNull(authDiskSource.getPinProtectedUserKeyEnvelope(userId = userId)) + assertNull(authDiskSource.getEphemeralPinProtectedUserKeyEnvelope(userId = userId)) + assertNull(authDiskSource.getPersistentPinProtectedUserKeyEnvelope(userId = userId)) } @Test @@ -813,13 +819,13 @@ class AuthDiskSourceTest { @Test @Suppress("MaxLineLength") - fun `storePinProtectedUserKeyEnvelope should update result flow from getPinProtectedUserKeyEnvelopeFlow`() = + fun `storePersistentPinProtectedUserKeyEnvelope should update result flow from getPersistentPinProtectedUserKeyEnvelopeFlow`() = runTest { val topSecretKey = "topsecret" val mockUserId = "mockUserId" - authDiskSource.getPinProtectedUserKeyEnvelopeFlow(mockUserId).test { + authDiskSource.getPersistentPinProtectedUserKeyEnvelopeFlow(mockUserId).test { assertNull(awaitItem()) - authDiskSource.storePinProtectedUserKeyEnvelope( + authDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = mockUserId, pinProtectedUserKeyEnvelope = topSecretKey, ) @@ -829,17 +835,16 @@ class AuthDiskSourceTest { @Test @Suppress("MaxLineLength") - fun `storePinProtectedUserKeyEnvelope with inMemoryOnly true emits flow and stores only in memory`() = + fun `storeEphemeralPinProtectedUserKeyEnvelope emits flow and stores only in memory`() = runTest { val userId = "mockUserId" val envelope = "topSecretEnvelope" - authDiskSource.getPinProtectedUserKeyEnvelopeFlow(userId).test { + authDiskSource.getEphemeralPinProtectedUserKeyEnvelopeFlow(userId).test { assertNull(awaitItem()) - authDiskSource.storePinProtectedUserKeyEnvelope( + authDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( userId = userId, pinProtectedUserKeyEnvelope = envelope, - inMemoryOnly = true, ) assertEquals(envelope, awaitItem()) assertEquals(envelope, authDiskSource.getPinProtectedUserKeyEnvelope(userId)) @@ -878,7 +883,7 @@ class AuthDiskSourceTest { "bwPreferencesStorage:pinKeyEncryptedUserKeyEnvelope" val mockUserId = "mockUserId" val mockPinProtectedUserKeyEnvelope = "mockPinProtectedUserKeyEnvelope" - authDiskSource.storePinProtectedUserKeyEnvelope( + authDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = mockUserId, pinProtectedUserKeyEnvelope = mockPinProtectedUserKeyEnvelope, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt index e6271872a3..8b8565945c 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/datasource/disk/util/FakeAuthDiskSource.kt @@ -65,8 +65,11 @@ class FakeAuthDiskSource : AuthDiskSource { private val storedLastLockTimestampState = mutableMapOf() private val storedAccountCryptographicState = mutableMapOf() - private val storedPinProtectedUserKeyEnvelopes = mutableMapOf>() - private val mutablePinProtectedUserKeyEnvelopesFlowMap = + private val storedEphemeralPinProtectedUserKeyEnvelopes = mutableMapOf() + private val storedPersistentPinProtectedUserKeyEnvelopes = mutableMapOf() + private val mutableEphemeralPinProtectedUserKeyEnvelopesFlowMap = + mutableMapOf>() + private val mutablePersistentPinProtectedUserKeyEnvelopesFlowMap = mutableMapOf>() override var userState: UserStateJson? = null @@ -90,7 +93,8 @@ class FakeAuthDiskSource : AuthDiskSource { storedBiometricInitVectors.remove(userId) storedBiometricKeys.remove(userId) storedOrganizationKeys.remove(userId) - storedPinProtectedUserKeyEnvelopes.remove(userId) + storedEphemeralPinProtectedUserKeyEnvelopes.remove(userId) + storedPersistentPinProtectedUserKeyEnvelopes.remove(userId) storedEncryptedPins.remove(userId) storedPinProtectedUserKeys.remove(userId) @@ -98,13 +102,8 @@ class FakeAuthDiskSource : AuthDiskSource { mutableOrganizationsFlowMap.remove(userId) mutablePoliciesFlowMap.remove(userId) mutableAccountTokensFlowMap.remove(userId) - mutablePinProtectedUserKeyEnvelopesFlowMap.remove(userId) - } - - private fun getMutablePinProtectedUserKeyEnvelopeFlow( - userId: String, - ): MutableSharedFlow = mutablePinProtectedUserKeyEnvelopesFlowMap.getOrPut(userId) { - bufferedMutableSharedFlow(replay = 1) + mutableEphemeralPinProtectedUserKeyEnvelopesFlowMap.remove(userId) + mutablePersistentPinProtectedUserKeyEnvelopesFlowMap.remove(userId) } override fun getShouldUseKeyConnectorFlow( @@ -175,15 +174,15 @@ class FakeAuthDiskSource : AuthDiskSource { } @Deprecated( - "Use getPinProtectedUserKeyEnvelope instead.", - replaceWith = ReplaceWith("getPinProtectedUserKeyEnvelope"), + "Use getEphemeralPinProtectedUserKeyEnvelope and " + + "getPersistentPinProtectedUserKeyEnvelope instead.", ) override fun getPinProtectedUserKey(userId: String): String? = storedPinProtectedUserKeys[userId]?.first @Deprecated( - "Use storePinProtectedUserKeyEnvelope instead.", - replaceWith = ReplaceWith("storePinProtectedUserKeyEnvelope"), + "Use storeEphemeralPinProtectedUserKeyEnvelope and " + + "storePersistentPinProtectedUserKeyEnvelope instead.", ) override fun storePinProtectedUserKey( userId: String, @@ -195,8 +194,8 @@ class FakeAuthDiskSource : AuthDiskSource { } @Deprecated( - "Use getPinProtectedUserKeyEnvelopeFlow instead.", - replaceWith = ReplaceWith("getPinProtectedUserKeyEnvelopeFlow"), + "Use getEphemeralPinProtectedUserKeyEnvelopeFlow and " + + "getPersistentPinProtectedUserKeyEnvelopeFlow instead.", ) override fun getPinProtectedUserKeyFlow(userId: String): Flow = getMutablePinProtectedUserKeyFlow(userId) @@ -347,40 +346,63 @@ class FakeAuthDiskSource : AuthDiskSource { storedLastLockTimestampState[userId] = lastLockTimestamp } - override fun getPinProtectedUserKeyEnvelope(userId: String): String? = - storedPinProtectedUserKeyEnvelopes[userId]?.first + override fun getPinProtectedUserKeyEnvelope( + userId: String, + ): String? = getEphemeralPinProtectedUserKeyEnvelope(userId = userId) + ?: getPersistentPinProtectedUserKeyEnvelope(userId = userId) - override fun storePinProtectedUserKeyEnvelope( + override fun getEphemeralPinProtectedUserKeyEnvelope( + userId: String, + ): String? = storedEphemeralPinProtectedUserKeyEnvelopes[userId] + + override fun getPersistentPinProtectedUserKeyEnvelope( + userId: String, + ): String? = storedPersistentPinProtectedUserKeyEnvelopes[userId] + + override fun storeEphemeralPinProtectedUserKeyEnvelope( userId: String, pinProtectedUserKeyEnvelope: String?, - inMemoryOnly: Boolean, ) { - storedPinProtectedUserKeyEnvelopes[userId] = pinProtectedUserKeyEnvelope to inMemoryOnly - getMutablePinProtectedUserKeyEnvelopeFlow(userId).tryEmit(pinProtectedUserKeyEnvelope) + storedEphemeralPinProtectedUserKeyEnvelopes[userId] = pinProtectedUserKeyEnvelope + getMutableEphemeralPinProtectedUserKeyEnvelopeFlow(userId = userId) + .tryEmit(pinProtectedUserKeyEnvelope) } - override fun getPinProtectedUserKeyEnvelopeFlow(userId: String): Flow = - getMutablePinProtectedUserKeyEnvelopeFlow(userId) - .onSubscription { - emit(getPinProtectedUserKeyEnvelope(userId)) - } + override fun storePersistentPinProtectedUserKeyEnvelope( + userId: String, + pinProtectedUserKeyEnvelope: String?, + ) { + storedPersistentPinProtectedUserKeyEnvelopes[userId] = pinProtectedUserKeyEnvelope + getMutablePersistentPinProtectedUserKeyEnvelopeFlow(userId = userId) + .tryEmit(pinProtectedUserKeyEnvelope) + } + + override fun getEphemeralPinProtectedUserKeyEnvelopeFlow( + userId: String, + ): Flow = getMutableEphemeralPinProtectedUserKeyEnvelopeFlow(userId = userId) + .onSubscription { emit(getEphemeralPinProtectedUserKeyEnvelope(userId = userId)) } + + override fun getPersistentPinProtectedUserKeyEnvelopeFlow( + userId: String, + ): Flow = getMutablePersistentPinProtectedUserKeyEnvelopeFlow(userId = userId) + .onSubscription { emit(getPersistentPinProtectedUserKeyEnvelope(userId = userId)) } /** - * Assert the the [isTdeLoginComplete] was stored successfully using the [userId]. + * Assert the [isTdeLoginComplete] was stored successfully using the [userId]. */ fun assertIsTdeLoginComplete(userId: String, isTdeLoginComplete: Boolean?) { assertEquals(isTdeLoginComplete, storedIsTdeLoginComplete[userId]) } /** - * Assert the the [shouldTrustDevice] was stored successfully using the [userId]. + * Assert the [shouldTrustDevice] was stored successfully using the [userId]. */ fun assertShouldTrustDevice(userId: String, shouldTrustDevice: Boolean?) { assertEquals(shouldTrustDevice, storedShouldTrustDevice[userId]) } /** - * Assert the the [shouldUseKeyConnector] was stored successfully using the [userId]. + * Assert the [shouldUseKeyConnector] was stored successfully using the [userId]. */ fun assertShouldUseKeyConnector(userId: String, shouldUseKeyConnector: Boolean?) { assertEquals(shouldUseKeyConnector, storedShouldUseKeyConnector[userId]) @@ -450,21 +472,35 @@ class FakeAuthDiskSource : AuthDiskSource { } /** - * Assert that the [pinProtectedUserKeyEnvelope] was stored successfully using the [userId]. + * Assert that the ephemeral [pinProtectedUserKeyEnvelope] was stored successfully using the + * [userId]. */ - fun assertPinProtectedUserKeyEnvelope( + fun assertEphemeralPinProtectedUserKeyEnvelope( userId: String, pinProtectedUserKeyEnvelope: String?, - inMemoryOnly: Boolean = false, ) { assertEquals( - pinProtectedUserKeyEnvelope to inMemoryOnly, - storedPinProtectedUserKeyEnvelopes[userId], + pinProtectedUserKeyEnvelope, + storedEphemeralPinProtectedUserKeyEnvelopes[userId], ) } /** - * Assert the the [organizationKeys] was stored successfully using the [userId]. + * Assert that the persistent [pinProtectedUserKeyEnvelope] was stored successfully using the + * [userId]. + */ + fun assertPersistentPinProtectedUserKeyEnvelope( + userId: String, + pinProtectedUserKeyEnvelope: String?, + ) { + assertEquals( + pinProtectedUserKeyEnvelope, + storedPersistentPinProtectedUserKeyEnvelopes[userId], + ) + } + + /** + * Assert the [organizationKeys] was stored successfully using the [userId]. */ fun assertOrganizationKeys(userId: String, organizationKeys: Map?) { assertEquals(organizationKeys, storedOrganizationKeys[userId]) @@ -587,5 +623,17 @@ class FakeAuthDiskSource : AuthDiskSource { bufferedMutableSharedFlow(replay = 1) } + private fun getMutableEphemeralPinProtectedUserKeyEnvelopeFlow( + userId: String, + ): MutableSharedFlow = mutableEphemeralPinProtectedUserKeyEnvelopesFlowMap.getOrPut( + key = userId, + ) { bufferedMutableSharedFlow(replay = 1) } + + private fun getMutablePersistentPinProtectedUserKeyEnvelopeFlow( + userId: String, + ): MutableSharedFlow = mutablePersistentPinProtectedUserKeyEnvelopesFlowMap.getOrPut( + key = userId, + ) { bufferedMutableSharedFlow(replay = 1) } + //endregion Private helper functions } diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerTest.kt index 00166467e2..12050c61a9 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserLogoutManagerTest.kt @@ -152,10 +152,19 @@ class UserLogoutManagerTest { settingsDiskSource.getVaultTimeoutAction(userId = userId) } returns vaultTimeoutAction every { - authDiskSource.getPinProtectedUserKeyEnvelope(userId = userId) + authDiskSource.getEphemeralPinProtectedUserKeyEnvelope(userId = userId) } returns pinProtectedUserKeyEnvelope every { - authDiskSource.storePinProtectedUserKeyEnvelope( + authDiskSource.getPersistentPinProtectedUserKeyEnvelope(userId = userId) + } returns pinProtectedUserKeyEnvelope + every { + authDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, + ) + } just runs + every { + authDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = userId, pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, ) @@ -193,7 +202,11 @@ class UserLogoutManagerTest { userId = userId, vaultTimeoutAction = vaultTimeoutAction, ) - authDiskSource.storePinProtectedUserKeyEnvelope( + authDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, + ) + authDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = userId, pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, ) @@ -221,10 +234,19 @@ class UserLogoutManagerTest { settingsDiskSource.getVaultTimeoutAction(userId = userId) } returns vaultTimeoutAction every { - authDiskSource.getPinProtectedUserKeyEnvelope(userId = userId) + authDiskSource.getEphemeralPinProtectedUserKeyEnvelope(userId = userId) } returns pinProtectedUserKeyEnvelope every { - authDiskSource.storePinProtectedUserKeyEnvelope( + authDiskSource.getPersistentPinProtectedUserKeyEnvelope(userId = userId) + } returns pinProtectedUserKeyEnvelope + every { + authDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, + ) + } just runs + every { + authDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = userId, pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, ) @@ -252,7 +274,11 @@ class UserLogoutManagerTest { userId = userId, vaultTimeoutAction = vaultTimeoutAction, ) - authDiskSource.storePinProtectedUserKeyEnvelope( + authDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, + ) + authDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = userId, pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserStateManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserStateManagerTest.kt index f62e9c85d3..8457293ca7 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserStateManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/manager/UserStateManagerTest.kt @@ -100,11 +100,11 @@ class UserStateManagerTest { ) fakeAuthDiskSource.apply { - storePinProtectedUserKeyEnvelope( + storePersistentPinProtectedUserKeyEnvelope( userId = USER_ID_1, pinProtectedUserKeyEnvelope = "pinProtectedUseKey", ) - storePinProtectedUserKeyEnvelope( + storePersistentPinProtectedUserKeyEnvelope( userId = USER_ID_2, pinProtectedUserKeyEnvelope = "pinProtectedUseKey", ) @@ -147,11 +147,11 @@ class UserStateManagerTest { ) fakeAuthDiskSource.apply { - storePinProtectedUserKeyEnvelope( + storePersistentPinProtectedUserKeyEnvelope( userId = USER_ID_1, pinProtectedUserKeyEnvelope = null, ) - storePinProtectedUserKeyEnvelope( + storePersistentPinProtectedUserKeyEnvelope( userId = USER_ID_2, pinProtectedUserKeyEnvelope = null, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt index 870793ec82..53ad2bbc7b 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryTest.kt @@ -6707,7 +6707,7 @@ class AuthRepositoryTest { val pinProtectedUserKey = "pinProtectedUserKey" val error = Throwable("Fail!") fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = SINGLE_USER_STATE_1.activeUserId, pinProtectedUserKeyEnvelope = pinProtectedUserKey, ) @@ -6741,7 +6741,7 @@ class AuthRepositoryTest { val pin = "PIN" val pinProtectedUserKey = "pinProtectedUserKey" fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = SINGLE_USER_STATE_1.activeUserId, pinProtectedUserKeyEnvelope = pinProtectedUserKey, ) @@ -6775,7 +6775,7 @@ class AuthRepositoryTest { val pin = "PIN" val pinProtectedUserKey = "pinProtectedUserKey" fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = SINGLE_USER_STATE_1.activeUserId, pinProtectedUserKeyEnvelope = pinProtectedUserKey, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt index 67c49a395a..97afc9e6ce 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/SettingsRepositoryTest.kt @@ -210,7 +210,7 @@ class SettingsRepositoryTest { // Updating the Vault settings values and calling setDefaultsIfNecessary again has no // effect on the currently stored values since we have a way to unlock the vault. - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = "pinProtectedKey", ) @@ -1050,10 +1050,9 @@ class SettingsRepositoryTest { userId = USER_ID, encryptedPin = userKeyEncryptedPin, ) - assertPinProtectedUserKeyEnvelope( + assertEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, - inMemoryOnly = true, ) } coVerify { @@ -1092,10 +1091,9 @@ class SettingsRepositoryTest { userId = USER_ID, encryptedPin = userKeyEncryptedPin, ) - assertPinProtectedUserKeyEnvelope( + assertPersistentPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, - inMemoryOnly = false, ) } coVerify { @@ -1115,7 +1113,11 @@ class SettingsRepositoryTest { userId = USER_ID, encryptedPin = "encryptedPin", ) - storePinProtectedUserKeyEnvelope( + storeEphemeralPinProtectedUserKeyEnvelope( + userId = USER_ID, + pinProtectedUserKeyEnvelope = "pinProtectedUserKeyEnvelope", + ) + storePersistentPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = "pinProtectedUserKeyEnvelope", ) @@ -1128,7 +1130,11 @@ class SettingsRepositoryTest { userId = USER_ID, encryptedPin = null, ) - assertPinProtectedUserKeyEnvelope( + assertEphemeralPinProtectedUserKeyEnvelope( + userId = USER_ID, + pinProtectedUserKeyEnvelope = null, + ) + assertPersistentPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = null, ) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/PinProtectedUserKeyManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/PinProtectedUserKeyManagerTest.kt index a27f990587..a39cd227be 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/PinProtectedUserKeyManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/PinProtectedUserKeyManagerTest.kt @@ -41,7 +41,7 @@ class PinProtectedUserKeyManagerTest { fun `deriveTemporaryPinProtectedUserKeyIfNecessary with encrypted key and existing pinProtectedUserKeyEnvelope does nothing`() = runTest { fakeAuthDiskSource.storeEncryptedPin(userId = USER_ID, encryptedPin = "encryptedPin") - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = "pinProtectedUserKeyEnvelope", ) @@ -67,7 +67,7 @@ class PinProtectedUserKeyManagerTest { userKeyEncryptedPin = userKeyEncryptedPin, ) fakeAuthDiskSource.storeEncryptedPin(userId = USER_ID, encryptedPin = encryptedPin) - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = null, ) @@ -92,10 +92,9 @@ class PinProtectedUserKeyManagerTest { userId = USER_ID, encryptedPin = userKeyEncryptedPin, ) - fakeAuthDiskSource.assertPinProtectedUserKeyEnvelope( + fakeAuthDiskSource.assertEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, - inMemoryOnly = true, ) fakeAuthDiskSource.assertPinProtectedUserKey( userId = USER_ID, @@ -106,12 +105,12 @@ class PinProtectedUserKeyManagerTest { @Suppress("MaxLineLength") @Test - fun `deriveTemporaryPinProtectedUserKeyIfNecessary with enrollment failure should clear all pin data`() = + fun `deriveTemporaryPinProtectedUserKeyIfNecessary with enrollment failure should clear ephemeral pin data`() = runTest { val encryptedPin = "encryptedPin" val error = Throwable("Fail!") fakeAuthDiskSource.storeEncryptedPin(userId = USER_ID, encryptedPin = encryptedPin) - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = null, ) @@ -136,10 +135,9 @@ class PinProtectedUserKeyManagerTest { userId = USER_ID, encryptedPin = null, ) - fakeAuthDiskSource.assertPinProtectedUserKeyEnvelope( + fakeAuthDiskSource.assertEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = null, - inMemoryOnly = false, ) fakeAuthDiskSource.assertPinProtectedUserKey( userId = USER_ID, @@ -162,10 +160,27 @@ class PinProtectedUserKeyManagerTest { @Suppress("MaxLineLength") @Test - fun `migratePinProtectedUserKeyIfNeeded with encrypted key and existing pinProtectedUserKeyEnvelope does nothing`() = + fun `migratePinProtectedUserKeyIfNeeded with encrypted key and existing PersistentPinProtectedUserKeyEnvelope does nothing`() = runTest { fakeAuthDiskSource.storeEncryptedPin(userId = USER_ID, encryptedPin = "encryptedPin") - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( + userId = USER_ID, + pinProtectedUserKeyEnvelope = "pinProtectedUserKeyEnvelope", + ) + + pinProtectedUserKeyManager.migratePinProtectedUserKeyIfNeeded(userId = USER_ID) + + coVerify(exactly = 0) { + vaultSdkSource.enrollPinWithEncryptedPin(userId = any(), encryptedPin = any()) + } + } + + @Suppress("MaxLineLength") + @Test + fun `migratePinProtectedUserKeyIfNeeded with encrypted key and existing ephemeralPinProtectedUserKeyEnvelope does nothing`() = + runTest { + fakeAuthDiskSource.storeEncryptedPin(userId = USER_ID, encryptedPin = "encryptedPin") + fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = "pinProtectedUserKeyEnvelope", ) @@ -189,7 +204,7 @@ class PinProtectedUserKeyManagerTest { userKeyEncryptedPin = userKeyEncryptedPin, ) fakeAuthDiskSource.storeEncryptedPin(userId = USER_ID, encryptedPin = encryptedPin) - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = null, ) @@ -216,10 +231,9 @@ class PinProtectedUserKeyManagerTest { userId = USER_ID, encryptedPin = userKeyEncryptedPin, ) - fakeAuthDiskSource.assertPinProtectedUserKeyEnvelope( + fakeAuthDiskSource.assertEphemeralPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = pinProtectedUserKeyEnvelope, - inMemoryOnly = true, ) fakeAuthDiskSource.assertPinProtectedUserKey( userId = USER_ID, @@ -236,7 +250,7 @@ class PinProtectedUserKeyManagerTest { val pinProtectedUserKey = "pinProtectedUserKey" val error = Throwable("Fail!") fakeAuthDiskSource.storeEncryptedPin(userId = USER_ID, encryptedPin = encryptedPin) - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = null, ) @@ -263,10 +277,9 @@ class PinProtectedUserKeyManagerTest { userId = USER_ID, encryptedPin = null, ) - fakeAuthDiskSource.assertPinProtectedUserKeyEnvelope( + fakeAuthDiskSource.assertPersistentPinProtectedUserKeyEnvelope( userId = USER_ID, pinProtectedUserKeyEnvelope = null, - inMemoryOnly = false, ) fakeAuthDiskSource.assertPinProtectedUserKey( userId = USER_ID, diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt index 2426d27eaf..7956627c67 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryTest.kt @@ -675,7 +675,11 @@ class VaultRepositoryTest { userId = "mockId-1", pinProtectedUserKey = null, ) - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( + userId = "mockId-1", + pinProtectedUserKeyEnvelope = null, + ) + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = "mockId-1", pinProtectedUserKeyEnvelope = null, ) @@ -699,7 +703,11 @@ class VaultRepositoryTest { userId = "mockId-1", pinProtectedUserKey = "mockKey-1", ) - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( + userId = "mockId-1", + pinProtectedUserKeyEnvelope = null, + ) + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = "mockId-1", pinProtectedUserKeyEnvelope = null, ) @@ -754,7 +762,11 @@ class VaultRepositoryTest { val mockVaultUnlockResult = VaultUnlockResult.Success prepareStateForUnlocking(unlockResult = mockVaultUnlockResult) - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storeEphemeralPinProtectedUserKeyEnvelope( + userId = userId, + pinProtectedUserKeyEnvelope = null, + ) + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = userId, pinProtectedUserKeyEnvelope = null, ) @@ -1585,7 +1597,7 @@ class VaultRepositoryTest { userId = userId, pinProtectedUserKey = "mockKey-1", ) - fakeAuthDiskSource.storePinProtectedUserKeyEnvelope( + fakeAuthDiskSource.storePersistentPinProtectedUserKeyEnvelope( userId = userId, pinProtectedUserKeyEnvelope = "mockKey-1", )