From 9349b235bc2b25bf4a56635529e82df69847ccce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bispo?= Date: Thu, 4 Dec 2025 10:53:40 +0000 Subject: [PATCH] [PM-27290] Remove password unlock method (#6176) --- .../auth/repository/AuthRepositoryImpl.kt | 18 +- .../vault/manager/VaultLockManagerImpl.kt | 22 +-- .../vault/repository/VaultRepositoryImpl.kt | 22 +-- .../util/InitUserCryptoMethodExtensions.kt | 6 +- .../auth/repository/AuthRepositoryTest.kt | 122 +++++++------ .../vault/manager/VaultLockManagerTest.kt | 166 +++++++++--------- .../vault/repository/VaultRepositoryTest.kt | 76 ++++---- 7 files changed, 212 insertions(+), 220 deletions(-) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index 26af426919..8a1e15e5c0 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -1883,21 +1883,15 @@ class AuthRepositoryImpl( // Attempt to unlock the vault with password if possible. val masterPassword = password ?: return null val privateKey = loginResponse.privateKeyOrNull() ?: return null - val key = loginResponse.key ?: return null - val initUserCryptoMethod = loginResponse + val masterPasswordUnlock = loginResponse .userDecryptionOptions ?.masterPasswordUnlock - ?.let { masterPasswordUnlock -> - InitUserCryptoMethod.MasterPasswordUnlock( - password = masterPassword, - masterPasswordUnlock = masterPasswordUnlock.toSdkMasterPasswordUnlock(), - ) - } - ?: InitUserCryptoMethod.Password( - password = masterPassword, - userKey = key, - ) + ?: return null + val initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( + password = masterPassword, + masterPasswordUnlock = masterPasswordUnlock.toSdkMasterPasswordUnlock(), + ) return unlockVault( accountProfile = profile, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt index c3a7a36217..85c416558d 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerImpl.kt @@ -259,29 +259,19 @@ class VaultLockManagerImpl( kdf: Kdf, userId: String, ) { - if (initUserCryptoMethod is InitUserCryptoMethod.Password || - initUserCryptoMethod is InitUserCryptoMethod.MasterPasswordUnlock - ) { - val password = when (initUserCryptoMethod) { - is InitUserCryptoMethod.Password -> initUserCryptoMethod.password - is InitUserCryptoMethod.MasterPasswordUnlock -> initUserCryptoMethod.password - else -> throw IllegalStateException( - "Invalid initUserCryptoMethod ${initUserCryptoMethod.logTag}.", - ) - } - + (initUserCryptoMethod as? InitUserCryptoMethod.MasterPasswordUnlock)?.let { // Save the master password hash. authSdkSource .hashPassword( email = email, - password = password, + password = initUserCryptoMethod.password, kdf = kdf, purpose = HashPurpose.LOCAL_AUTHORIZATION, ) - .onSuccess { passwordHash -> + .onSuccess { authDiskSource.storeMasterPasswordHash( userId = userId, - passwordHash = passwordHash, + passwordHash = it, ) } } @@ -713,15 +703,15 @@ class VaultLockManagerImpl( private suspend fun updateKdfIfNeeded(initUserCryptoMethod: InitUserCryptoMethod) { val password = when (initUserCryptoMethod) { - is InitUserCryptoMethod.Password -> initUserCryptoMethod.password is InitUserCryptoMethod.MasterPasswordUnlock -> initUserCryptoMethod.password is InitUserCryptoMethod.AuthRequest, is InitUserCryptoMethod.DecryptedKey, is InitUserCryptoMethod.DeviceKey, is InitUserCryptoMethod.KeyConnector, + is InitUserCryptoMethod.Password, is InitUserCryptoMethod.Pin, is InitUserCryptoMethod.PinEnvelope, - -> return + -> return } kdfManager diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt index 69d1b1d991..e3e04755d4 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepositoryImpl.kt @@ -341,25 +341,19 @@ class VaultRepositoryImpl( ): VaultUnlockResult { val userId = activeUserId ?: return VaultUnlockResult.InvalidStateError(error = NoActiveUserException()) - val userKey = authDiskSource.getUserKey(userId = userId) - ?: return VaultUnlockResult.InvalidStateError( - error = MissingPropertyException("User key"), - ) + val activeAccount = authDiskSource.userState?.activeAccount - val initUserCryptoMethod = activeAccount + val masterPasswordUnlock = activeAccount ?.profile ?.userDecryptionOptions ?.masterPasswordUnlock - ?.let { masterPasswordUnlock -> - InitUserCryptoMethod.MasterPasswordUnlock( - password = masterPassword, - masterPasswordUnlock = masterPasswordUnlock.toSdkMasterPasswordUnlock(), - ) - } - ?: InitUserCryptoMethod.Password( - password = masterPassword, - userKey = userKey, + ?: return VaultUnlockResult.InvalidStateError( + error = MissingPropertyException("MasterPasswordUnlock data"), ) + val initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( + password = masterPassword, + masterPasswordUnlock = masterPasswordUnlock.toSdkMasterPasswordUnlock(), + ) return this .unlockVaultForUser( userId = userId, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/InitUserCryptoMethodExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/InitUserCryptoMethodExtensions.kt index b497bcc62d..883bab242d 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/InitUserCryptoMethodExtensions.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/InitUserCryptoMethodExtensions.kt @@ -12,8 +12,12 @@ val InitUserCryptoMethod.logTag: String 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" + is InitUserCryptoMethod.Password -> { + // PM-27290: InitUserCryptoMethod.Password will be removed from the SDK in a future + // release. This else branch can be cleaned up afterwards. + throw IllegalArgumentException("Unsupported InitUserCryptoMethod: $this") + } } 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 9f925aae9a..aefb247147 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 @@ -28,6 +28,7 @@ import com.bitwarden.network.model.ConfigResponseJson import com.bitwarden.network.model.DeleteAccountResponseJson import com.bitwarden.network.model.GetTokenResponseJson import com.bitwarden.network.model.IdentityTokenAuthModel +import com.bitwarden.network.model.KdfJson import com.bitwarden.network.model.KdfTypeJson import com.bitwarden.network.model.KeyConnectorMasterKeyResponseJson import com.bitwarden.network.model.MasterPasswordUnlockDataJson @@ -415,9 +416,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -490,9 +491,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -643,7 +644,7 @@ class AuthRepositoryTest { @Test fun `delete account fails if deleteAccount fails`() = runTest { val masterPassword = "hello world" - val hashedMasterPassword = "dlrow olleh" + val hashedMasterPassword = "hashed password" fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 val kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams() val error = Throwable("Fail") @@ -675,7 +676,7 @@ class AuthRepositoryTest { @Test fun `delete account fails if deleteAccount fails with message`() = runTest { val masterPassword = "hello world" - val hashedMasterPassword = "dlrow olleh" + val hashedMasterPassword = "hashed password" fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 val kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams() coEvery { @@ -709,7 +710,7 @@ class AuthRepositoryTest { @Test fun `deleteAccountWithMasterPassword succeeds`() = runTest { val masterPassword = "hello world" - val hashedMasterPassword = "dlrow olleh" + val hashedMasterPassword = "hashed password" fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 val kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams() coEvery { @@ -1818,9 +1819,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -1874,9 +1875,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -1927,9 +1928,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -1990,9 +1991,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -2132,9 +2133,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -2198,9 +2199,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -2326,9 +2327,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -2428,9 +2429,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -2497,9 +2498,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -2550,9 +2551,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -6824,9 +6825,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -6897,9 +6898,9 @@ class AuthRepositoryTest { securityState = successResponse.accountKeys ?.securityState ?.securityState, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, - userKey = successResponse.key!!, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, ), organizationKeys = null, ) @@ -7111,10 +7112,24 @@ class AuthRepositoryTest { shouldResetMasterPassword = true, twoFactorToken = null, masterPasswordPolicyOptions = null, - userDecryptionOptions = null, + userDecryptionOptions = UserDecryptionOptionsJson( + hasMasterPassword = true, + trustedDeviceUserDecryptionOptions = null, + keyConnectorUserDecryptionOptions = null, + masterPasswordUnlock = MasterPasswordUnlockDataJson( + kdf = KdfJson( + kdfType = KdfTypeJson.ARGON2_ID, + iterations = 600000, + memory = 16, + parallelism = 4, + ), + masterKeyWrappedUserKey = "key", + salt = "mockSalt", + ), + ), keyConnectorUrl = null, ) - private val PROFILE_1 = AccountJson.Profile( + private val BASE_PROFILE_1 = AccountJson.Profile( userId = USER_ID_1, email = EMAIL, isEmailVerified = true, @@ -7132,6 +7147,19 @@ class AuthRepositoryTest { isTwoFactorEnabled = false, creationDate = ZonedDateTime.parse("2024-09-13T01:00:00.00Z"), ) + + private val PROFILE_1 = BASE_PROFILE_1.copy( + userDecryptionOptions = UserDecryptionOptionsJson( + hasMasterPassword = true, + trustedDeviceUserDecryptionOptions = null, + keyConnectorUserDecryptionOptions = null, + masterPasswordUnlock = MasterPasswordUnlockDataJson( + kdf = BASE_PROFILE_1.toSdkParams().toKdfRequestModel(), + masterKeyWrappedUserKey = "key", + salt = "mockSalt", + ), + ), + ) private val ACCOUNT_1 = AccountJson( profile = PROFILE_1, settings = AccountJson.Settings( @@ -7176,33 +7204,21 @@ class AuthRepositoryTest { hasMasterPassword = true, keyConnectorUserDecryptionOptions = null, trustedDeviceUserDecryptionOptions = null, - masterPasswordUnlock = null, + masterPasswordUnlock = MasterPasswordUnlockDataJson( + kdf = BASE_PROFILE_1.toSdkParams().toKdfRequestModel(), + masterKeyWrappedUserKey = "key", + salt = "mockSalt", + ), ), ), ), ), ) - private val MOCK_MASTER_PASSWORD_UNLOCK_DATA = MasterPasswordUnlockDataJson( + private val MOCK_MASTER_PASSWORD_UNLOCK = MasterPasswordUnlockData( + kdf = ACCOUNT_1.profile.toSdkParams(), + masterKeyWrappedUserKey = "key", salt = "mockSalt", - kdf = ACCOUNT_2.profile.toSdkParams().toKdfRequestModel(), - masterKeyWrappedUserKey = "masterKeyWrappedUserKeyMock", - ) - - private val SINGLE_USER_STATE_1_WITH_DECRYPTION_OPTIONS = UserStateJson( - activeUserId = USER_ID_1, - accounts = mapOf( - USER_ID_1 to ACCOUNT_1.copy( - profile = ACCOUNT_1.profile.copy( - userDecryptionOptions = UserDecryptionOptionsJson( - hasMasterPassword = true, - keyConnectorUserDecryptionOptions = null, - trustedDeviceUserDecryptionOptions = null, - masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, - ), - ), - ), - ), ) private val SINGLE_USER_STATE_2 = UserStateJson( diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt index 355ae034f0..6af0823878 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManagerTest.kt @@ -15,7 +15,6 @@ import com.bitwarden.core.data.manager.realtime.RealtimeManager import com.bitwarden.core.data.util.asFailure import com.bitwarden.core.data.util.asSuccess import com.bitwarden.crypto.HashPurpose -import com.bitwarden.crypto.Kdf import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.AccountTokensJson import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson @@ -80,7 +79,7 @@ class VaultLockManagerTest { coEvery { hashPassword( email = MOCK_PROFILE.email, - password = "drowssap", + password = "mockValue", kdf = MOCK_PROFILE.toSdkParams(), purpose = HashPurpose.LOCAL_AUTHORIZATION, ) @@ -913,8 +912,7 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { @@ -925,9 +923,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -960,9 +958,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -994,9 +992,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1017,8 +1015,7 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") val userAutoUnlockKey = "userAutoUnlockKey" @@ -1030,9 +1027,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1074,9 +1071,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -1110,9 +1107,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1133,8 +1130,7 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") val error = Throwable("Fail") @@ -1146,9 +1142,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1172,9 +1168,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -1196,9 +1192,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1213,8 +1209,7 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { @@ -1225,9 +1220,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1258,9 +1253,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -1282,9 +1277,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1305,8 +1300,7 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") val error = Throwable("Fail") @@ -1318,9 +1312,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1343,9 +1337,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -1367,9 +1361,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1384,8 +1378,7 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { @@ -1396,9 +1389,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1428,9 +1421,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -1452,9 +1445,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1475,8 +1468,7 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") coEvery { @@ -1487,9 +1479,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1519,9 +1511,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -1550,9 +1542,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1649,8 +1641,7 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") val userKeyEncryptedPin = "encryptedPin" @@ -1667,9 +1658,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1698,9 +1689,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -1724,9 +1715,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1747,16 +1738,12 @@ class VaultLockManagerTest { runTest { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = mapOf("orgId1" to "orgKey1") val initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - masterPasswordUnlock = MasterPasswordUnlockData( - kdf = mockk(relaxed = true), - masterKeyWrappedUserKey = "mockKey", - salt = "mockSalt", - ), + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ) coEvery { vaultSdkSource.initializeCrypto( @@ -1865,8 +1852,8 @@ class VaultLockManagerTest { private suspend fun verifyUnlockingVault(userId: String) { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = null coEvery { @@ -1877,9 +1864,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1897,9 +1884,13 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MasterPasswordUnlockData( + kdf = kdf, + masterKeyWrappedUserKey = userKey, + salt = "mockSalt", + ), ), organizationKeys = organizationKeys, ) @@ -1911,8 +1902,7 @@ class VaultLockManagerTest { private suspend fun verifyUnlockedVault(userId: String) { val kdf = MOCK_PROFILE.toSdkParams() val email = MOCK_PROFILE.email - val masterPassword = "drowssap" - val userKey = "12345" + val masterPassword = "mockValue" val privateKey = "54321" val organizationKeys = null val userAutoUnlockKey = "userAutoUnlockKey" @@ -1927,9 +1917,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -1950,9 +1940,9 @@ class VaultLockManagerTest { privateKey = privateKey, signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), organizationKeys = organizationKeys, ) @@ -1966,9 +1956,9 @@ class VaultLockManagerTest { kdfParams = kdf, email = email, privateKey = privateKey, - method = InitUserCryptoMethod.Password( + method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, - userKey = userKey, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), signingKey = null, securityState = null, @@ -2053,3 +2043,9 @@ private val MOCK_USER_STATE = UserStateJson( USER_ID to MOCK_ACCOUNT, ), ) + +private val MOCK_MASTER_PASSWORD_UNLOCK_DATA = MasterPasswordUnlockData( + kdf = MOCK_PROFILE.toSdkParams(), + masterKeyWrappedUserKey = "12345", + salt = "mockSalt", +) 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 c7271852db..7abb2b63ba 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 @@ -11,6 +11,7 @@ import com.bitwarden.core.data.util.asFailure import com.bitwarden.core.data.util.asSuccess import com.bitwarden.exporters.ExportFormat import com.bitwarden.fido.Fido2CredentialAutofillView +import com.bitwarden.core.MasterPasswordUnlockData import com.bitwarden.network.model.CipherTypeJson import com.bitwarden.network.model.MasterPasswordUnlockDataJson import com.bitwarden.network.model.SyncResponseJson @@ -536,9 +537,13 @@ class VaultRepositoryTest { privateKey = "mockPrivateKey-1", signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = "mockPassword-1", - userKey = "mockKey-1", + masterPasswordUnlock = MasterPasswordUnlockData( + kdf = MOCK_PROFILE.toSdkParams(), + masterKeyWrappedUserKey = "mockKey-1", + salt = "mockSalt-1", + ), ), organizationKeys = createMockOrganizationKeys(number = 1), ) @@ -618,7 +623,7 @@ class VaultRepositoryTest { @Suppress("MaxLineLength") @Test - fun `unlockVaultWithMasterPassword without masterPasswordUnlock data should use Password method`() = + fun `unlockVaultWithMasterPassword without masterPasswordUnlock data should return InvalidStateError`() = runTest { val userId = "mockId-1" val masterPassword = "mockPassword-1" @@ -636,41 +641,13 @@ class VaultRepositoryTest { fakeAuthDiskSource.storePrivateKey(userId = userId, privateKey = "mockPrivateKey-1") fakeAuthDiskSource.storeUserKey(userId = userId, userKey = userKey) - coEvery { - vaultLockManager.unlockVault( - userId = userId, - email = "email", - kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( - password = masterPassword, - userKey = userKey, - ), - organizationKeys = null, - ) - } returns VaultUnlockResult.Success - val result = vaultRepository.unlockVaultWithMasterPassword( masterPassword = masterPassword, ) - assertEquals(VaultUnlockResult.Success, result) - coVerify { - vaultLockManager.unlockVault( - userId = userId, - email = "email", - kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( - password = masterPassword, - userKey = userKey, - ), - organizationKeys = null, - ) + assertTrue(result is VaultUnlockResult.InvalidStateError) + coVerify(exactly = 0) { + vaultLockManager.unlockVault(any(), any(), any(), any(), any(), any(), any(), any()) } } @@ -698,9 +675,13 @@ class VaultRepositoryTest { privateKey = "mockPrivateKey-1", signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = "mockPassword-1", - userKey = "mockKey-1", + masterPasswordUnlock = MasterPasswordUnlockData( + kdf = MOCK_PROFILE.toSdkParams(), + masterKeyWrappedUserKey = "mockKey-1", + salt = "mockSalt-1", + ), ), organizationKeys = createMockOrganizationKeys(number = 1), ) @@ -1460,9 +1441,13 @@ class VaultRepositoryTest { privateKey = "mockPrivateKey-1", signingKey = null, securityState = null, - initUserCryptoMethod = InitUserCryptoMethod.Password( + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = mockMasterPassword, - userKey = "mockKey-1", + masterPasswordUnlock = MasterPasswordUnlockData( + kdf = MOCK_PROFILE.toSdkParams(), + masterKeyWrappedUserKey = "mockKey-1", + salt = "mockSalt-1", + ), ), organizationKeys = createMockOrganizationKeys(number = 1), ) @@ -1505,7 +1490,7 @@ class VaultRepositoryTest { //endregion Helper functions } -private val MOCK_PROFILE = AccountJson.Profile( +private val MOCK_BASE_PROFILE = AccountJson.Profile( userId = "mockId-1", email = "email", isEmailVerified = true, @@ -1524,6 +1509,19 @@ private val MOCK_PROFILE = AccountJson.Profile( creationDate = ZonedDateTime.parse("2024-09-13T01:00:00.00Z"), ) +private val MOCK_PROFILE = MOCK_BASE_PROFILE.copy( + userDecryptionOptions = UserDecryptionOptionsJson( + hasMasterPassword = true, + trustedDeviceUserDecryptionOptions = null, + keyConnectorUserDecryptionOptions = null, + masterPasswordUnlock = MasterPasswordUnlockDataJson( + kdf = MOCK_BASE_PROFILE.toSdkParams().toKdfRequestModel(), + masterKeyWrappedUserKey = "mockKey-1", + salt = "mockSalt-1", + ), + ), +) + private val MOCK_ACCOUNT = AccountJson( profile = MOCK_PROFILE, tokens = AccountTokensJson(