PM-41062: chore: Separate ephemeral and persistent pin envelopes (#7218)

This commit is contained in:
David Perez
2026-07-28 19:42:38 +00:00
committed by GitHub
parent d87432e982
commit b0daf6631d
13 changed files with 374 additions and 188 deletions
@@ -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<String?>
/**
* 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<String?>
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<String?>
/**
* Retrieves a flow for the persistent pin-protected user key envelope for the given [userId].
*/
fun getPersistentPinProtectedUserKeyEnvelopeFlow(userId: String): Flow<String?>
/**
* Gets a two-factor auth token using a user's [email].
@@ -74,8 +74,11 @@ class AuthDiskSourceImpl(
),
AuthDiskSource {
private val inMemoryPinProtectedUserKeys = mutableMapOf<String, String?>()
private val inMemoryPinProtectedUserKeyEnvelopes = mutableMapOf<String, String?>()
// Ephemeral in-memory values
private val ephemeralPinProtectedUserKeys = mutableMapOf<String, String?>()
private val ephemeralPinProtectedUserKeyEnvelopes = mutableMapOf<String, String?>()
// Shared flows for observing data changes
private val mutableShouldUseKeyConnectorFlowMap =
mutableMapOf<String, MutableSharedFlow<Boolean?>>()
private val mutableOrganizationsFlowMap =
@@ -91,7 +94,9 @@ class AuthDiskSourceImpl(
mutableMapOf<String, MutableSharedFlow<String?>>()
private val mutablePinProtectedUserKeyFlowMap =
mutableMapOf<String, MutableSharedFlow<String?>>()
private val mutablePinProtectedUserKeyEnvelopeFlowMap =
private val mutableEphemeralPinProtectedUserKeyEnvelopeFlowMap =
mutableMapOf<String, MutableSharedFlow<String?>>()
private val mutablePersistentPinProtectedUserKeyEnvelopeFlowMap =
mutableMapOf<String, MutableSharedFlow<String?>>()
private val mutableUserStateFlow = bufferedMutableSharedFlow<UserStateJson?>(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<String?> =
getMutablePinProtectedUserKeyFlow(userId)
.onSubscription { emit(getPinProtectedUserKey(userId = userId)) }
override fun getPinProtectedUserKeyEnvelopeFlow(userId: String): Flow<String?> =
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<String?> = getMutableEphemeralPinProtectedUserKeyEnvelopeFlow(userId = userId)
.onSubscription { emit(getEphemeralPinProtectedUserKeyEnvelope(userId = userId)) }
override fun getPersistentPinProtectedUserKeyEnvelopeFlow(
userId: String,
): Flow<String?> = 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<String?> = mutablePinProtectedUserKeyEnvelopeFlowMap.getOrPut(userId) {
bufferedMutableSharedFlow(replay = 1)
}
): MutableSharedFlow<String?> = mutableEphemeralPinProtectedUserKeyEnvelopeFlowMap.getOrPut(
key = userId,
) { bufferedMutableSharedFlow(replay = 1) }
private fun getMutablePersistentPinProtectedUserKeyEnvelopeFlow(
userId: String,
): MutableSharedFlow<String?> = mutablePersistentPinProtectedUserKeyEnvelopeFlowMap.getOrPut(
key = userId,
) { bufferedMutableSharedFlow(replay = 1) }
private fun migrateAccountTokens() {
userState
@@ -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,
)
}
}
@@ -294,13 +294,15 @@ class SettingsRepositoryImpl(
override val isUnlockWithPinEnabledFlow: Flow<Boolean>
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)
}
}
@@ -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.
@@ -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,
)
@@ -65,8 +65,11 @@ class FakeAuthDiskSource : AuthDiskSource {
private val storedLastLockTimestampState = mutableMapOf<String, Instant?>()
private val storedAccountCryptographicState =
mutableMapOf<String, WrappedAccountCryptographicState?>()
private val storedPinProtectedUserKeyEnvelopes = mutableMapOf<String, Pair<String?, Boolean>>()
private val mutablePinProtectedUserKeyEnvelopesFlowMap =
private val storedEphemeralPinProtectedUserKeyEnvelopes = mutableMapOf<String, String?>()
private val storedPersistentPinProtectedUserKeyEnvelopes = mutableMapOf<String, String?>()
private val mutableEphemeralPinProtectedUserKeyEnvelopesFlowMap =
mutableMapOf<String, MutableSharedFlow<String?>>()
private val mutablePersistentPinProtectedUserKeyEnvelopesFlowMap =
mutableMapOf<String, MutableSharedFlow<String?>>()
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<String?> = 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<String?> =
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<String?> =
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<String?> = getMutableEphemeralPinProtectedUserKeyEnvelopeFlow(userId = userId)
.onSubscription { emit(getEphemeralPinProtectedUserKeyEnvelope(userId = userId)) }
override fun getPersistentPinProtectedUserKeyEnvelopeFlow(
userId: String,
): Flow<String?> = 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<String, String>?) {
assertEquals(organizationKeys, storedOrganizationKeys[userId])
@@ -587,5 +623,17 @@ class FakeAuthDiskSource : AuthDiskSource {
bufferedMutableSharedFlow(replay = 1)
}
private fun getMutableEphemeralPinProtectedUserKeyEnvelopeFlow(
userId: String,
): MutableSharedFlow<String?> = mutableEphemeralPinProtectedUserKeyEnvelopesFlowMap.getOrPut(
key = userId,
) { bufferedMutableSharedFlow(replay = 1) }
private fun getMutablePersistentPinProtectedUserKeyEnvelopeFlow(
userId: String,
): MutableSharedFlow<String?> = mutablePersistentPinProtectedUserKeyEnvelopesFlowMap.getOrPut(
key = userId,
) { bufferedMutableSharedFlow(replay = 1) }
//endregion Private helper functions
}
@@ -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,
)
@@ -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,
)
@@ -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,
)
@@ -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,
)
@@ -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,
@@ -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",
)