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 e42ea194f64..e9ce261edce 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 @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.repository import com.bitwarden.core.AuthRequestMethod import com.bitwarden.core.InitUserCryptoMethod +import com.bitwarden.core.WrappedAccountCryptographicState import com.bitwarden.core.data.manager.dispatcher.DispatcherManager import com.bitwarden.core.data.repository.error.MissingPropertyException import com.bitwarden.core.data.repository.util.bufferedMutableSharedFlow @@ -113,6 +114,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockError import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState import com.x8bit.bitwarden.data.vault.repository.util.toSdkMasterPasswordUnlock import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers @@ -517,6 +519,7 @@ class AuthRepositoryImpl( ) val signingKey = accountKeys?.signatureKeyPair?.wrappedSigningKey val securityState = accountKeys?.securityState?.securityState + val signedPublicKey = accountKeys?.publicKeyEncryptionKeyPair?.signedPublicKey checkForVaultUnlockError( onVaultUnlockError = { error -> @@ -524,10 +527,13 @@ class AuthRepositoryImpl( }, ) { unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = securityState, + signingKey = signingKey, + signedPublicKey = signedPublicKey, + ), accountProfile = profile, - privateKey = privateKey, - signingKey = signingKey, - securityState = securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), @@ -1809,14 +1815,23 @@ class AuthRepositoryImpl( ) .map { unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = loginResponse.accountKeys + ?.securityState + ?.securityState, + signingKey = loginResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + signedPublicKey = loginResponse.accountKeys + ?.publicKeyEncryptionKeyPair + ?.signedPublicKey, + ), accountProfile = profile, - privateKey = privateKey, initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( masterKey = it.masterKey, userKey = key, ), - securityState = loginResponse.accountKeys?.securityState?.securityState, - signingKey = loginResponse.accountKeys?.signatureKeyPair?.wrappedSigningKey, ) } .fold( @@ -1837,11 +1852,21 @@ class AuthRepositoryImpl( organizationIdentifier = orgIdentifier, ) .map { keyConnectorResponse -> + val accountKeys = loginResponse.accountKeys val result = unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = keyConnectorResponse.keys.private, + securityState = accountKeys + ?.securityState + ?.securityState, + signingKey = accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + signedPublicKey = accountKeys + ?.publicKeyEncryptionKeyPair + ?.signedPublicKey, + ), accountProfile = profile, - privateKey = keyConnectorResponse.keys.private, - securityState = loginResponse.accountKeys?.securityState?.securityState, - signingKey = loginResponse.accountKeys?.signatureKeyPair?.wrappedSigningKey, initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( masterKey = keyConnectorResponse.masterKey, userKey = keyConnectorResponse.encryptedUserKey, @@ -1897,10 +1922,19 @@ class AuthRepositoryImpl( ) return unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = loginResponse.accountKeys + ?.securityState + ?.securityState, + signingKey = loginResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + signedPublicKey = loginResponse.accountKeys + ?.publicKeyEncryptionKeyPair + ?.signedPublicKey, + ), accountProfile = profile, - privateKey = privateKey, - securityState = loginResponse.accountKeys?.securityState?.securityState, - signingKey = loginResponse.accountKeys?.signatureKeyPair?.wrappedSigningKey, initUserCryptoMethod = initUserCryptoMethod, ) } @@ -1908,6 +1942,7 @@ class AuthRepositoryImpl( /** * Attempt to unlock the current user's vault with trusted device specific data. */ + @Suppress("LongMethod") private suspend fun unlockVaultWithTdeOnLoginSuccess( loginResponse: GetTokenResponseJson.Success, profile: AccountJson.Profile, @@ -1920,10 +1955,19 @@ class AuthRepositoryImpl( if (privateKey != null && key != null) { deviceData?.let { model -> return unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = loginResponse.accountKeys + ?.securityState + ?.securityState, + signingKey = loginResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + signedPublicKey = loginResponse.accountKeys + ?.publicKeyEncryptionKeyPair + ?.signedPublicKey, + ), accountProfile = profile, - privateKey = privateKey, - securityState = loginResponse.accountKeys?.securityState?.securityState, - signingKey = loginResponse.accountKeys?.signatureKeyPair?.wrappedSigningKey, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = model.privateKey, method = model @@ -1953,9 +1997,18 @@ class AuthRepositoryImpl( unlockVaultWithTrustedDeviceUserDecryptionOptionsAndStoreKeys( options = options, profile = profile, - privateKey = accountKeys.publicKeyEncryptionKeyPair.wrappedPrivateKey, - securityState = accountKeys.securityState?.securityState, - signingKey = accountKeys.signatureKeyPair?.wrappedSigningKey, + privateKey = accountKeys + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = accountKeys + .securityState + ?.securityState, + signedPublicKey = accountKeys + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = accountKeys + .signatureKeyPair + ?.wrappedSigningKey, ) } ?: loginResponse.privateKey @@ -1965,6 +2018,7 @@ class AuthRepositoryImpl( profile = profile, privateKey = privateKey, securityState = null, + signedPublicKey = null, signingKey = null, ) } @@ -1980,6 +2034,7 @@ class AuthRepositoryImpl( profile: AccountJson.Profile, privateKey: String, securityState: String?, + signedPublicKey: String?, signingKey: String?, ): VaultUnlockResult? { var vaultUnlockResult: VaultUnlockResult? = null @@ -1997,10 +2052,13 @@ class AuthRepositoryImpl( // For approved requests the key will always be present. val userKey = requireNotNull(request.key) vaultUnlockResult = unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = securityState, + signingKey = signingKey, + signedPublicKey = signedPublicKey, + ), accountProfile = profile, - privateKey = privateKey, - signingKey = signingKey, - securityState = securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = pendingRequest.requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = userKey), @@ -2026,10 +2084,13 @@ class AuthRepositoryImpl( } vaultUnlockResult = unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = securityState, + signingKey = signingKey, + signedPublicKey = signedPublicKey, + ), accountProfile = profile, - privateKey = privateKey, - securityState = securityState, - signingKey = signingKey, initUserCryptoMethod = InitUserCryptoMethod.DeviceKey( deviceKey = deviceKey, protectedDevicePrivateKey = encryptedPrivateKey, @@ -2047,20 +2108,16 @@ class AuthRepositoryImpl( * A helper function to unlock the vault for the user associated with the [accountProfile]. */ private suspend fun unlockVault( + accountCryptographicState: WrappedAccountCryptographicState, accountProfile: AccountJson.Profile, - privateKey: String, - securityState: String?, - signingKey: String?, initUserCryptoMethod: InitUserCryptoMethod, ): VaultUnlockResult { val userId = accountProfile.userId return vaultRepository.unlockVault( + accountCryptographicState = accountCryptographicState, userId = userId, email = accountProfile.email, kdf = accountProfile.toSdkParams(), - privateKey = privateKey, - signingKey = signingKey, - securityState = securityState, initUserCryptoMethod = initUserCryptoMethod, // The value for the organization keys here will typically be null. We can separately // unlock the vault for organization data after receiving the sync response if this diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/manager/BitwardenCredentialManagerImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/manager/BitwardenCredentialManagerImpl.kt index 9ee8c56eea7..31e82a1892d 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/manager/BitwardenCredentialManagerImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/credentials/manager/BitwardenCredentialManagerImpl.kt @@ -258,6 +258,7 @@ class BitwardenCredentialManagerImpl( userId = userId, fido2CredentialStore = fido2CredentialStore, relyingPartyId = relyingPartyId, + userHandle = null, ) .fold( onSuccess = { it }, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryImpl.kt index 28a300a04b1..be334449c0c 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryImpl.kt @@ -16,6 +16,7 @@ import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource import com.x8bit.bitwarden.data.vault.datasource.sdk.ScopedVaultSdkSource import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult @@ -137,17 +138,21 @@ class AuthenticatorBridgeRepositoryImpl( ?.securityState ?.securityState val signingKey = accountKeys?.signatureKeyPair?.wrappedSigningKey + val signedPublicKey = accountKeys?.publicKeyEncryptionKeyPair?.signedPublicKey return scopedVaultSdkSource .initializeCrypto( userId = userId, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = securityState, + signingKey = signingKey, + signedPublicKey = signedPublicKey, + ), userId = userId, kdfParams = account.profile.toSdkParams(), email = account.profile.email, - privateKey = privateKey, - securityState = securityState, - signingKey = signingKey, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = decryptedUserKey, ), diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt index d061d7e71a5..c2c2cd10b8f 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt @@ -487,6 +487,7 @@ interface VaultSdkSource { userId: String, fido2CredentialStore: Fido2CredentialStore, relyingPartyId: String, + userHandle: String?, ): Result> /** diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt index 131d1844427..2f18c9e91c7 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt @@ -599,6 +599,7 @@ class VaultSdkSourceImpl( userId: String, fido2CredentialStore: Fido2CredentialStore, relyingPartyId: String, + userHandle: String?, ): Result> = runCatchingWithLogs { getClient(userId) .platform() @@ -607,7 +608,7 @@ class VaultSdkSourceImpl( userInterface = Fido2CredentialSearchUserInterfaceImpl(), credentialStore = fido2CredentialStore, ) - .silentlyDiscoverCredentials(relyingPartyId) + .silentlyDiscoverCredentials(relyingPartyId, userHandle?.toByteArray()) } override suspend fun makeUpdateKdf( diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialAuthenticationUserInterfaceImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialAuthenticationUserInterfaceImpl.kt index 1836dc26e36..c38b1bfaf26 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialAuthenticationUserInterfaceImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialAuthenticationUserInterfaceImpl.kt @@ -28,7 +28,7 @@ class Fido2CredentialAuthenticationUserInterfaceImpl( newCredential: Fido2CredentialNewView, ): CheckUserAndPickCredentialForCreationResult = throw IllegalStateException() - override suspend fun isVerificationEnabled(): Boolean = isVerificationSupported + override fun isVerificationEnabled(): Boolean = isVerificationSupported override suspend fun pickCredentialForAuthentication( availableCredentials: List, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialRegistrationUserInterfaceImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialRegistrationUserInterfaceImpl.kt index 0afab79b1e4..9be84bd30ce 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialRegistrationUserInterfaceImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialRegistrationUserInterfaceImpl.kt @@ -32,7 +32,7 @@ class Fido2CredentialRegistrationUserInterfaceImpl( checkUserResult = CheckUserResult(userPresent = true, userVerified = true), ) - override suspend fun isVerificationEnabled(): Boolean = isVerificationSupported + override fun isVerificationEnabled(): Boolean = isVerificationSupported override suspend fun pickCredentialForAuthentication( availableCredentials: List, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialSearchUserInterfaceImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialSearchUserInterfaceImpl.kt index ff1614901af..0501f6709c0 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialSearchUserInterfaceImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialSearchUserInterfaceImpl.kt @@ -29,7 +29,7 @@ class Fido2CredentialSearchUserInterfaceImpl : Fido2UserInterface { // Always return true for this property because any problems with verification should // be handled downstream where the app can actually offer verification methods. - override suspend fun isVerificationEnabled(): Boolean = true + override fun isVerificationEnabled(): Boolean = true override suspend fun pickCredentialForAuthentication( availableCredentials: List, diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialStoreImpl.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialStoreImpl.kt index 8f0f596be20..793cb1675cf 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialStoreImpl.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/Fido2CredentialStoreImpl.kt @@ -42,7 +42,11 @@ class Fido2CredentialStoreImpl( * @param ids Optional list of FIDO 2 credential ID's to find. * @param ripId Relying Party ID to find. */ - override suspend fun findCredentials(ids: List?, ripId: String): List = + override suspend fun findCredentials( + ids: List?, + ripId: String, + userHandle: ByteArray?, + ): List = vaultRepository .decryptCipherListResultStateFlow .value diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt index 53fc1f91538..2718cabc391 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/manager/VaultLockManager.kt @@ -1,6 +1,7 @@ package com.x8bit.bitwarden.data.vault.manager import com.bitwarden.core.InitUserCryptoMethod +import com.bitwarden.core.WrappedAccountCryptographicState import com.bitwarden.crypto.Kdf import com.bitwarden.sdk.AuthClient import com.x8bit.bitwarden.data.vault.manager.model.VaultStateEvent @@ -61,12 +62,10 @@ interface VaultLockManager { */ @Suppress("LongParameterList") suspend fun unlockVault( + accountCryptographicState: WrappedAccountCryptographicState, userId: String, email: String, kdf: Kdf, - privateKey: String, - signingKey: String?, - securityState: String?, initUserCryptoMethod: InitUserCryptoMethod, organizationKeys: Map?, ): VaultUnlockResult 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 b422ffd5922..8c8ee0a31b0 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 @@ -7,6 +7,7 @@ import android.content.IntentFilter import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.InitUserCryptoRequest +import com.bitwarden.core.WrappedAccountCryptographicState import com.bitwarden.core.data.manager.dispatcher.DispatcherManager import com.bitwarden.core.data.manager.realtime.RealtimeManager import com.bitwarden.core.data.repository.error.MissingPropertyException @@ -39,6 +40,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResul import com.x8bit.bitwarden.data.vault.manager.model.VaultStateEvent import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState import com.x8bit.bitwarden.data.vault.repository.util.logTag import com.x8bit.bitwarden.data.vault.repository.util.statusFor import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult @@ -171,12 +173,10 @@ class VaultLockManagerImpl( @Suppress("LongMethod") override suspend fun unlockVault( + accountCryptographicState: WrappedAccountCryptographicState, userId: String, email: String, kdf: Kdf, - privateKey: String, - signingKey: String?, - securityState: String?, initUserCryptoMethod: InitUserCryptoMethod, organizationKeys: Map?, ): VaultUnlockResult = withContext(context = NonCancellable) { @@ -187,13 +187,11 @@ class VaultLockManagerImpl( .initializeCrypto( userId = userId, request = InitUserCryptoRequest( + accountCryptographicState = accountCryptographicState, kdfParams = kdf, email = email, - privateKey = privateKey, method = initUserCryptoMethod, userId = userId, - signingKey = signingKey, - securityState = securityState, ), ) .flatMap { result -> @@ -683,14 +681,18 @@ class VaultLockManagerImpl( ) val signingKey = accountKeys?.signatureKeyPair?.wrappedSigningKey val securityState = accountKeys?.securityState?.securityState + val signedPublicKey = accountKeys?.publicKeyEncryptionKeyPair?.signedPublicKey val organizationKeys = authDiskSource.getOrganizationKeys(userId = userId) return unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = securityState, + signingKey = signingKey, + signedPublicKey = signedPublicKey, + ), userId = userId, email = account.profile.email, kdf = account.profile.toSdkParams(), - privateKey = privateKey, - signingKey = signingKey, - securityState = securityState, initUserCryptoMethod = initUserCryptoMethod, organizationKeys = organizationKeys, ) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt index a525e84aae0..a52bdf042b3 100644 --- a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/VaultRepository.kt @@ -92,6 +92,7 @@ interface VaultRepository : userId: String, fido2CredentialStore: Fido2CredentialStore, relyingPartyId: String, + userHandle: String?, ): Result> /** 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 15d6819fd72..0a9ee3c8fd9 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 @@ -41,6 +41,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.GenerateTotpResult import com.x8bit.bitwarden.data.vault.repository.model.ImportCredentialsResult import com.x8bit.bitwarden.data.vault.repository.model.TotpCodeResult import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolder import com.x8bit.bitwarden.data.vault.repository.util.toSdkAccount @@ -253,12 +254,14 @@ class VaultRepositoryImpl( userId: String, fido2CredentialStore: Fido2CredentialStore, relyingPartyId: String, + userHandle: String?, ): Result> = vaultSdkSource .silentlyDiscoverCredentials( userId = userId, fido2CredentialStore = fido2CredentialStore, relyingPartyId = relyingPartyId, + userHandle = userHandle, ) override fun emitTotpCodeResult(totpCodeResult: TotpCodeResult) { @@ -537,15 +540,19 @@ class VaultRepositoryImpl( ) val signingKey = accountKeys?.signatureKeyPair?.wrappedSigningKey val securityState = accountKeys?.securityState?.securityState + val signedPublicKey = accountKeys?.publicKeyEncryptionKeyPair?.signedPublicKey val organizationKeys = authDiskSource .getOrganizationKeys(userId = userId) return vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = securityState, + signingKey = signingKey, + signedPublicKey = signedPublicKey, + ), userId = userId, email = account.profile.email, kdf = account.profile.toSdkParams(), - privateKey = privateKey, - signingKey = signingKey, - securityState = securityState, initUserCryptoMethod = initUserCryptoMethod, organizationKeys = organizationKeys, ) diff --git a/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/WrappedAccountCryptographicStateExtensions.kt b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/WrappedAccountCryptographicStateExtensions.kt new file mode 100644 index 00000000000..b1c948e877a --- /dev/null +++ b/app/src/main/kotlin/com/x8bit/bitwarden/data/vault/repository/util/WrappedAccountCryptographicStateExtensions.kt @@ -0,0 +1,34 @@ +package com.x8bit.bitwarden.data.vault.repository.util + +import com.bitwarden.core.WrappedAccountCryptographicState + +/** + * Creates a [WrappedAccountCryptographicState] based on the available cryptographic parameters. + * + * Returns [WrappedAccountCryptographicState.V2] if signing key, signed public key, and security + * state are all present, otherwise returns [WrappedAccountCryptographicState.V1]. + * + * @param privateKey The user's wrapped private key. + * @param securityState The user's signed security state (V2 only). + * @param signingKey The user's wrapped signing key (V2 only). + * @param signedPublicKey The user's signed public key (V2 only). + */ +fun createWrappedAccountCryptographicState( + privateKey: String, + securityState: String?, + signingKey: String?, + signedPublicKey: String?, +): WrappedAccountCryptographicState { + return if (signingKey != null && securityState != null && signedPublicKey != null) { + WrappedAccountCryptographicState.V2( + privateKey = privateKey, + securityState = securityState, + signingKey = signingKey, + signedPublicKey = signedPublicKey, + ) + } else { + WrappedAccountCryptographicState.V1( + privateKey = privateKey, + ) + } +} 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 61ad768f0ba..e9b3856aa7e 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 @@ -59,6 +59,7 @@ import com.bitwarden.network.model.VerifiedOrganizationDomainSsoDetailsResponse import com.bitwarden.network.model.VerifyEmailTokenRequestJson import com.bitwarden.network.model.VerifyEmailTokenResponseJson import com.bitwarden.network.model.createMockAccountKeysJson +import com.bitwarden.network.model.createMockAccountKeysJsonWithNullFields import com.bitwarden.network.model.createMockOrganization import com.bitwarden.network.model.createMockPolicy import com.bitwarden.network.service.AccountsService @@ -133,6 +134,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource import com.x8bit.bitwarden.data.vault.repository.VaultRepository import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState import io.mockk.coEvery import io.mockk.coVerify import io.mockk.every @@ -406,18 +408,23 @@ class AuthRepositoryTest { } returns successResponse.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -481,18 +488,23 @@ class AuthRepositoryTest { uniqueAppId = UNIQUE_APP_ID, ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -1413,12 +1425,15 @@ class AuthRepositoryTest { fakeAuthDiskSource.storeAccountKeys(userId = USER_ID_1, accountKeys = null) coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), @@ -1434,12 +1449,15 @@ class AuthRepositoryTest { ) coVerify(exactly = 1) { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), @@ -1467,12 +1485,15 @@ class AuthRepositoryTest { fakeAuthDiskSource.storeOrganizationKeys(userId = USER_ID_1, organizationKeys = orgKeys) coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = accountKeys.publicKeyEncryptionKeyPair.wrappedPrivateKey, + securityState = accountKeys.securityState?.securityState, + signedPublicKey = accountKeys.publicKeyEncryptionKeyPair.signedPublicKey, + signingKey = accountKeys.signatureKeyPair?.wrappedSigningKey, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = accountKeys.publicKeyEncryptionKeyPair.wrappedPrivateKey, - signingKey = accountKeys.signatureKeyPair?.wrappedSigningKey, - securityState = accountKeys.securityState?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricKey), @@ -1489,12 +1510,15 @@ class AuthRepositoryTest { coVerify(exactly = 1) { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = accountKeys.publicKeyEncryptionKeyPair.wrappedPrivateKey, + securityState = accountKeys.securityState?.securityState, + signedPublicKey = accountKeys.publicKeyEncryptionKeyPair.signedPublicKey, + signingKey = accountKeys.signatureKeyPair?.wrappedSigningKey, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = accountKeys.publicKeyEncryptionKeyPair.wrappedPrivateKey, - signingKey = accountKeys.signatureKeyPair?.wrappedSigningKey, - securityState = accountKeys.securityState?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricKey), @@ -1520,12 +1544,15 @@ class AuthRepositoryTest { fakeAuthDiskSource.storeOrganizationKeys(userId = USER_ID_1, organizationKeys = orgKeys) coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), @@ -1542,12 +1569,15 @@ class AuthRepositoryTest { coVerify(exactly = 1) { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), @@ -1572,12 +1602,15 @@ class AuthRepositoryTest { val error = Throwable("Fail") coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), @@ -1594,12 +1627,15 @@ class AuthRepositoryTest { coVerify(exactly = 1) { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), @@ -1614,6 +1650,65 @@ class AuthRepositoryTest { assertEquals(LoginResult.Error(errorMessage = null, error = error), result) } + @Suppress("MaxLineLength") + @Test + fun `completeTdeLogin with accountKeys with null nested fields should unlock vault with null properties`() = + runTest { + val requestPrivateKey = "requestPrivateKey" + val asymmetricalKey = "asymmetricalKey" + val accountKeys = ACCOUNT_KEYS_WITH_NULL_FIELDS + val orgKeys = mapOf("orgId" to "orgKey") + fakeAuthDiskSource.userState = SINGLE_USER_STATE_1 + fakeAuthDiskSource.storeAccountKeys(userId = USER_ID_1, accountKeys = accountKeys) + fakeAuthDiskSource.storeOrganizationKeys(userId = USER_ID_1, organizationKeys = orgKeys) + coEvery { + vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = accountKeys.publicKeyEncryptionKeyPair.wrappedPrivateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), + userId = USER_ID_1, + email = SINGLE_USER_STATE_1.activeAccount.profile.email, + kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), + initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( + requestPrivateKey = requestPrivateKey, + method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), + ), + organizationKeys = orgKeys, + ) + } returns VaultUnlockResult.Success + coEvery { vaultRepository.syncIfNecessary() } just runs + + val result = repository.completeTdeLogin( + requestPrivateKey = requestPrivateKey, + asymmetricalKey = asymmetricalKey, + ) + + coVerify(exactly = 1) { + vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = accountKeys.publicKeyEncryptionKeyPair.wrappedPrivateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), + userId = USER_ID_1, + email = SINGLE_USER_STATE_1.activeAccount.profile.email, + kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), + initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( + requestPrivateKey = requestPrivateKey, + method = AuthRequestMethod.UserKey(protectedUserKey = asymmetricalKey), + ), + organizationKeys = orgKeys, + ) + vaultRepository.syncIfNecessary() + settingsRepository.storeUserHasLoggedInValue(userId = USER_ID_1) + } + assertEquals(LoginResult.Success, result) + } + @Test fun `login when pre login fails should return Error with no message`() = runTest { val error = RuntimeException() @@ -1809,18 +1904,23 @@ class AuthRepositoryTest { } returns successResponse.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -1865,18 +1965,130 @@ class AuthRepositoryTest { uniqueAppId = UNIQUE_APP_ID, ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), + userId = USER_ID_1, + email = EMAIL, + kdf = ACCOUNT_1.profile.toSdkParams(), + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( + password = PASSWORD, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, + ), + organizationKeys = null, + ) + vaultRepository.syncIfNecessary() + settingsRepository.storeUserHasLoggedInValue(userId = USER_ID_1) + } + assertEquals( + SINGLE_USER_STATE_1, + fakeAuthDiskSource.userState, + ) + verify(exactly = 1) { + userStateManager.hasPendingAccountAddition = false + settingsRepository.setDefaultsIfNecessary(userId = USER_ID_1) + } + } + + @Test + @Suppress("MaxLineLength") + fun `login get token succeeds with accountKeys with null nested fields should unlock vault with null properties`() = + runTest { + val successResponse = GET_TOKEN_WITH_ACCOUNT_KEYS_RESPONSE_SUCCESS.copy( + accountKeys = ACCOUNT_KEYS_WITH_NULL_FIELDS, + ) + coEvery { + identityService.preLogin(email = EMAIL) + } returns PRE_LOGIN_SUCCESS.asSuccess() + coEvery { + identityService.getToken( + email = EMAIL, + authModel = IdentityTokenAuthModel.MasterPassword( + username = EMAIL, + password = PASSWORD_HASH, + ), + uniqueAppId = UNIQUE_APP_ID, + ) + } returns successResponse.asSuccess() + coEvery { + vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), + userId = USER_ID_1, + email = EMAIL, + kdf = ACCOUNT_1.profile.toSdkParams(), + initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( + password = PASSWORD, + masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, + ), + organizationKeys = null, + ) + } returns VaultUnlockResult.Success + coEvery { vaultRepository.syncIfNecessary() } just runs + every { + successResponse.toUserState( + previousUserState = null, + environmentUrlData = EnvironmentUrlDataJson.DEFAULT_US, + ) + } returns SINGLE_USER_STATE_1 + val result = repository.login(email = EMAIL, password = PASSWORD) + assertEquals(LoginResult.Success, result) + assertEquals(AuthState.Authenticated(ACCESS_TOKEN), repository.authStateFlow.value) + coVerify { identityService.preLogin(email = EMAIL) } + fakeAuthDiskSource.assertPrivateKey( + userId = USER_ID_1, + privateKey = "privateKey", + ) + fakeAuthDiskSource.assertAccountKeys( + userId = USER_ID_1, + accountKeys = ACCOUNT_KEYS_WITH_NULL_FIELDS, + ) + fakeAuthDiskSource.assertUserKey( + userId = USER_ID_1, + userKey = "key", + ) + fakeAuthDiskSource.assertMasterPasswordHash( + userId = USER_ID_1, + passwordHash = PASSWORD_HASH, + ) + coVerify { + identityService.getToken( + email = EMAIL, + authModel = IdentityTokenAuthModel.MasterPassword( + username = EMAIL, + password = PASSWORD_HASH, + ), + uniqueAppId = UNIQUE_APP_ID, + ) + vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -1918,18 +2130,23 @@ class AuthRepositoryTest { val error = Throwable("Fail") coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -1981,18 +2198,23 @@ class AuthRepositoryTest { ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -2080,18 +2302,23 @@ class AuthRepositoryTest { } coVerify(exactly = 0) { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = any(), organizationKeys = null, ) @@ -2123,18 +2350,23 @@ class AuthRepositoryTest { } returns successResponse.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -2189,18 +2421,23 @@ class AuthRepositoryTest { uniqueAppId = UNIQUE_APP_ID, ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -2317,18 +2554,23 @@ class AuthRepositoryTest { } returns successResponse.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -2419,18 +2661,23 @@ class AuthRepositoryTest { val error = Throwable("Fail") coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -2488,18 +2735,23 @@ class AuthRepositoryTest { } returns successResponse.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -2541,18 +2793,23 @@ class AuthRepositoryTest { twoFactorData = rememberedTwoFactorData, ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -2737,18 +2994,23 @@ class AuthRepositoryTest { } returns SINGLE_USER_STATE_1 coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = DEVICE_REQUEST_PRIVATE_KEY, method = AuthRequestMethod.MasterKey( @@ -2793,18 +3055,23 @@ class AuthRepositoryTest { ) vaultRepository.syncIfNecessary() vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = DEVICE_REQUEST_PRIVATE_KEY, method = AuthRequestMethod.MasterKey( @@ -2851,18 +3118,23 @@ class AuthRepositoryTest { } returns SINGLE_USER_STATE_1 coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = DEVICE_REQUEST_PRIVATE_KEY, method = AuthRequestMethod.MasterKey( @@ -2907,18 +3179,23 @@ class AuthRepositoryTest { ) vaultRepository.syncIfNecessary() vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = DEVICE_REQUEST_PRIVATE_KEY, method = AuthRequestMethod.MasterKey( @@ -3057,18 +3334,23 @@ class AuthRepositoryTest { } returns SINGLE_USER_STATE_1 coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = SINGLE_USER_STATE_1.activeUserId, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = DEVICE_REQUEST_PRIVATE_KEY, method = AuthRequestMethod.MasterKey( @@ -3403,12 +3685,15 @@ class AuthRepositoryTest { } returns keyConnectorMasterKeyResponseJson.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "privateKey", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = "privateKey", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( masterKey = masterKey, userKey = "key", @@ -3450,12 +3735,125 @@ class AuthRepositoryTest { accessToken = ACCESS_TOKEN, ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "privateKey", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), + userId = USER_ID_1, + email = EMAIL, + kdf = ACCOUNT_1.profile.toSdkParams(), + initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( + masterKey = masterKey, + userKey = "key", + ), + organizationKeys = null, + ) + vaultRepository.syncIfNecessary() + } + assertEquals(SINGLE_USER_STATE_1, fakeAuthDiskSource.userState) + verify(exactly = 1) { + userStateManager.hasPendingAccountAddition = false + settingsRepository.setDefaultsIfNecessary(userId = USER_ID_1) + } + } + + @Test + @Suppress("MaxLineLength") + fun `SSO login get token succeeds with key connector and accountKeys with null nested fields should unlock vault`() = + runTest { + val keyConnectorUrl = "www.example.com" + val successResponse = GET_TOKEN_RESPONSE_SUCCESS.copy( + keyConnectorUrl = keyConnectorUrl, + accountKeys = ACCOUNT_KEYS_WITH_NULL_FIELDS, + userDecryptionOptions = USER_DECRYPTION_OPTIONS.copy( + hasMasterPassword = false, + trustedDeviceUserDecryptionOptions = null, + ), + ) + val masterKey = "masterKey" + val keyConnectorMasterKeyResponseJson = mockk { + every { this@mockk.masterKey } returns masterKey + } + coEvery { + identityService.getToken( + email = EMAIL, + authModel = IdentityTokenAuthModel.SingleSignOn( + ssoCode = SSO_CODE, + ssoCodeVerifier = SSO_CODE_VERIFIER, + ssoRedirectUri = SSO_REDIRECT_URI, + ), + uniqueAppId = UNIQUE_APP_ID, + ) + } returns successResponse.asSuccess() + coEvery { + keyConnectorManager.getMasterKeyFromKeyConnector( + url = keyConnectorUrl, + accessToken = ACCESS_TOKEN, + ) + } returns keyConnectorMasterKeyResponseJson.asSuccess() + coEvery { + vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "privateKey", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), + userId = USER_ID_1, + email = EMAIL, + kdf = ACCOUNT_1.profile.toSdkParams(), + initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( + masterKey = masterKey, + userKey = "key", + ), + organizationKeys = null, + ) + } returns VaultUnlockResult.Success + coEvery { vaultRepository.syncIfNecessary() } just runs + every { + successResponse.toUserState( + previousUserState = null, + environmentUrlData = EnvironmentUrlDataJson.DEFAULT_US, + ) + } returns SINGLE_USER_STATE_1 + val result = repository.login( + email = EMAIL, + ssoCode = SSO_CODE, + ssoCodeVerifier = SSO_CODE_VERIFIER, + ssoRedirectUri = SSO_REDIRECT_URI, + organizationIdentifier = ORGANIZATION_IDENTIFIER, + ) + + assertEquals(LoginResult.Success, result) + assertEquals(AuthState.Authenticated(ACCESS_TOKEN), repository.authStateFlow.value) + fakeAuthDiskSource.assertPrivateKey(userId = USER_ID_1, privateKey = "privateKey") + fakeAuthDiskSource.assertUserKey(userId = USER_ID_1, userKey = "key") + coVerify(exactly = 1) { + identityService.getToken( + email = EMAIL, + authModel = IdentityTokenAuthModel.SingleSignOn( + ssoCode = SSO_CODE, + ssoCodeVerifier = SSO_CODE_VERIFIER, + ssoRedirectUri = SSO_REDIRECT_URI, + ), + uniqueAppId = UNIQUE_APP_ID, + ) + keyConnectorManager.getMasterKeyFromKeyConnector( + url = keyConnectorUrl, + accessToken = ACCESS_TOKEN, + ) + vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "privateKey", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = "privateKey", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( masterKey = masterKey, userKey = "key", @@ -3600,12 +3998,15 @@ class AuthRepositoryTest { } returns keyConnectorResponse.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = PRIVATE_KEY, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( masterKey = masterKey, userKey = ENCRYPTED_USER_KEY, @@ -3656,12 +4057,15 @@ class AuthRepositoryTest { organizationIdentifier = ORGANIZATION_IDENTIFIER, ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "privateKey", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = "privateKey", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( masterKey = masterKey, userKey = ENCRYPTED_USER_KEY, @@ -3776,12 +4180,15 @@ class AuthRepositoryTest { coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = PRIVATE_KEY, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.KeyConnector( masterKey = masterKey, userKey = ENCRYPTED_USER_KEY, @@ -3850,18 +4257,23 @@ class AuthRepositoryTest { } returns SINGLE_USER_STATE_1 coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = DEVICE_REQUEST_PRIVATE_KEY, method = AuthRequestMethod.UserKey( @@ -3905,18 +4317,23 @@ class AuthRepositoryTest { ) vaultRepository.syncIfNecessary() vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = DEVICE_REQUEST_PRIVATE_KEY, method = AuthRequestMethod.UserKey( @@ -4017,18 +4434,23 @@ class AuthRepositoryTest { ) coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.DeviceKey( deviceKey = deviceKey, protectedDevicePrivateKey = encryptedPrivateKey, @@ -4082,18 +4504,23 @@ class AuthRepositoryTest { uniqueAppId = UNIQUE_APP_ID, ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.DeviceKey( deviceKey = deviceKey, protectedDevicePrivateKey = encryptedPrivateKey, @@ -4133,18 +4560,23 @@ class AuthRepositoryTest { fakeAuthDiskSource.storePendingAuthRequest(USER_ID_1, pendingAuthRequest) coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = pendingAuthRequest.requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = authRequestKey), @@ -4196,18 +4628,23 @@ class AuthRepositoryTest { uniqueAppId = UNIQUE_APP_ID, ) vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = SINGLE_USER_STATE_1.activeAccount.profile.email, kdf = SINGLE_USER_STATE_1.activeAccount.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.AuthRequest( requestPrivateKey = pendingAuthRequest.requestPrivateKey, method = AuthRequestMethod.UserKey(protectedUserKey = authRequestKey), @@ -6815,18 +7252,23 @@ class AuthRepositoryTest { } returns successResponse.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -6888,18 +7330,23 @@ class AuthRepositoryTest { } returns successResponse.asSuccess() coEvery { vaultRepository.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .wrappedPrivateKey, + securityState = successResponse.accountKeys + ?.securityState + ?.securityState, + signedPublicKey = successResponse.accountKeys!! + .publicKeyEncryptionKeyPair + .signedPublicKey, + signingKey = successResponse.accountKeys + ?.signatureKeyPair + ?.wrappedSigningKey, + ), userId = USER_ID_1, email = EMAIL, kdf = ACCOUNT_1.profile.toSdkParams(), - privateKey = successResponse.accountKeys!! - .publicKeyEncryptionKeyPair - .wrappedPrivateKey, - signingKey = successResponse.accountKeys - ?.signatureKeyPair - ?.wrappedSigningKey, - securityState = successResponse.accountKeys - ?.securityState - ?.securityState, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = PASSWORD, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK, @@ -7038,6 +7485,8 @@ class AuthRepositoryTest { private const val ORGANIZATION_IDENTIFIER = "organizationIdentifier" private val ORGANIZATIONS = listOf(createMockOrganization(number = 0)) private val ACCOUNT_KEYS = createMockAccountKeysJson(number = 1) + private val ACCOUNT_KEYS_WITH_NULL_FIELDS = + createMockAccountKeysJsonWithNullFields(number = 1) private val TWO_FACTOR_AUTH_METHODS_DATA = mapOf( TwoFactorAuthMethod.EMAIL to JsonObject( mapOf("Email" to JsonPrimitive("ex***@email.com")), diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/manager/BitwardenCredentialManagerTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/manager/BitwardenCredentialManagerTest.kt index caa355225cd..94060f0db96 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/manager/BitwardenCredentialManagerTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/credentials/manager/BitwardenCredentialManagerTest.kt @@ -1165,6 +1165,7 @@ class BitwardenCredentialManagerTest { userId = "mockUserId", fido2CredentialStore = any(), relyingPartyId = "mockRpId-1", + userHandle = null, ) } returns fido2CredentialAutofillViews.asSuccess() every { @@ -1304,6 +1305,7 @@ class BitwardenCredentialManagerTest { userId = "mockUserId", fido2CredentialStore = any(), relyingPartyId = "mockRpId-1", + userHandle = null, ) } returns fido2CredentialAutofillViews.asSuccess() every { @@ -1425,6 +1427,7 @@ class BitwardenCredentialManagerTest { userId = "mockUserId", fido2CredentialStore = any(), relyingPartyId = "mockRpId-1", + userHandle = null, ) } returns fido2CredentialAutofillViews.asSuccess() every { diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt index 99a0ff2a343..ef03d491019 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/platform/repository/AuthenticatorBridgeRepositoryTest.kt @@ -23,6 +23,7 @@ import com.x8bit.bitwarden.data.platform.repository.util.sanitizeTotpUri import com.x8bit.bitwarden.data.vault.datasource.disk.VaultDiskSource import com.x8bit.bitwarden.data.vault.datasource.sdk.ScopedVaultSdkSource import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult +import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher import io.mockk.coEvery import io.mockk.coVerify @@ -86,15 +87,18 @@ class AuthenticatorBridgeRepositoryTest { scopedVaultSdkSource.initializeCrypto( userId = USER_1_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = USER_1_PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_1_ID, kdfParams = Kdf.Argon2id(iterations = 0U, memory = 0U, parallelism = 0U), email = USER_1_EMAIL, - privateKey = USER_1_PRIVATE_KEY, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = USER_1_UNLOCK_KEY, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -102,15 +106,18 @@ class AuthenticatorBridgeRepositoryTest { scopedVaultSdkSource.initializeCrypto( userId = USER_2_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = USER_2_PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_2_ID, kdfParams = Kdf.Argon2id(iterations = 0U, memory = 0U, parallelism = 0U), email = USER_2_EMAIL, - privateKey = USER_2_PRIVATE_KEY, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = USER_2_UNLOCK_KEY, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -200,15 +207,18 @@ class AuthenticatorBridgeRepositoryTest { scopedVaultSdkSource.initializeCrypto( userId = USER_2_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = USER_2_PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_2_ID, kdfParams = Kdf.Argon2id(iterations = 0U, memory = 0U, parallelism = 0U), email = USER_2_EMAIL, - privateKey = USER_2_PRIVATE_KEY, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = USER_2_UNLOCK_KEY, ), - signingKey = null, - securityState = null, ), ) scopedVaultSdkSource.initializeOrganizationCrypto( @@ -239,15 +249,18 @@ class AuthenticatorBridgeRepositoryTest { scopedVaultSdkSource.initializeCrypto( userId = USER_1_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = USER_1_PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_1_ID, kdfParams = Kdf.Argon2id(iterations = 0U, memory = 0U, parallelism = 0U), email = USER_1_EMAIL, - privateKey = USER_1_PRIVATE_KEY, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = USER_1_UNLOCK_KEY, ), - signingKey = null, - securityState = null, ), ) scopedVaultSdkSource.initializeOrganizationCrypto( @@ -262,15 +275,18 @@ class AuthenticatorBridgeRepositoryTest { scopedVaultSdkSource.initializeCrypto( userId = USER_2_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = USER_2_PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_2_ID, kdfParams = Kdf.Argon2id(iterations = 0U, memory = 0U, parallelism = 0U), email = USER_2_EMAIL, - privateKey = USER_2_PRIVATE_KEY, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = USER_2_UNLOCK_KEY, ), - signingKey = null, - securityState = null, ), ) scopedVaultSdkSource.initializeOrganizationCrypto( @@ -297,15 +313,18 @@ class AuthenticatorBridgeRepositoryTest { scopedVaultSdkSource.initializeCrypto( userId = USER_1_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = USER_1_PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_1_ID, kdfParams = Kdf.Argon2id(iterations = 0U, memory = 0U, parallelism = 0U), email = USER_1_EMAIL, - privateKey = USER_1_PRIVATE_KEY, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = USER_1_UNLOCK_KEY, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.AuthenticationError(error = Throwable()).asSuccess() @@ -319,29 +338,35 @@ class AuthenticatorBridgeRepositoryTest { scopedVaultSdkSource.initializeCrypto( userId = USER_1_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = USER_1_PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_1_ID, kdfParams = Kdf.Argon2id(iterations = 0U, memory = 0U, parallelism = 0U), email = USER_1_EMAIL, - privateKey = USER_1_PRIVATE_KEY, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = USER_1_UNLOCK_KEY, ), - signingKey = null, - securityState = null, ), ) scopedVaultSdkSource.initializeCrypto( userId = USER_2_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = USER_2_PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_2_ID, kdfParams = Kdf.Argon2id(iterations = 0U, memory = 0U, parallelism = 0U), email = USER_2_EMAIL, - privateKey = USER_2_PRIVATE_KEY, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = USER_2_UNLOCK_KEY, ), - signingKey = null, - securityState = null, ), ) scopedVaultSdkSource.initializeOrganizationCrypto( diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt index 30fbf90a386..3925d97cd8f 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt @@ -1394,18 +1394,24 @@ class VaultSdkSourceTest { val userId = "userId" val fido2CredentialStore: Fido2CredentialStore = mockk() val relyingPartyId = "relyingPartyId" + val userHandle = "mockUserHandle" val mockAutofillView = Fido2CredentialAutofillView( credentialId = byteArrayOf(0), cipherId = "mockCipherId", rpId = "mockRpId", userNameForUi = "mockUserNameForUi", - userHandle = "mockUserHandle".toByteArray(), + userHandle = userHandle.toByteArray(), hasCounter = false, ) val autofillViews = listOf(mockAutofillView) val authenticator: ClientFido2Authenticator = mockk { - coEvery { silentlyDiscoverCredentials(relyingPartyId) } returns autofillViews + coEvery { + silentlyDiscoverCredentials( + relyingPartyId, + userHandle.toByteArray(), + ) + } returns autofillViews } every { clientFido2.authenticator( @@ -1418,6 +1424,7 @@ class VaultSdkSourceTest { userId = userId, fido2CredentialStore = fido2CredentialStore, relyingPartyId = relyingPartyId, + userHandle = userHandle, ) assertEquals( @@ -1432,6 +1439,7 @@ class VaultSdkSourceTest { val userId = "userId" val fido2CredentialStore: Fido2CredentialStore = mockk() val relyingPartyId = "relyingPartyId" + val userHandle = "mockUserHandle" coEvery { clientFido2 @@ -1439,7 +1447,10 @@ class VaultSdkSourceTest { userInterface = Fido2CredentialSearchUserInterfaceImpl(), credentialStore = fido2CredentialStore, ) - .silentlyDiscoverCredentials(relyingPartyId) + .silentlyDiscoverCredentials( + relyingPartyId, + userHandle.toByteArray(), + ) } throws BitwardenException.SilentlyDiscoverCredentials( mockk("mockException"), ) @@ -1448,6 +1459,7 @@ class VaultSdkSourceTest { userId = userId, fido2CredentialStore = fido2CredentialStore, relyingPartyId = relyingPartyId, + userHandle = userHandle, ) assertTrue(result.isFailure) diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/PublicKeyAuthenticatorAssertionResponseUtil.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/PublicKeyAuthenticatorAssertionResponseUtil.kt index 8f0cbbcdd96..2bc413f5703 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/PublicKeyAuthenticatorAssertionResponseUtil.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/PublicKeyAuthenticatorAssertionResponseUtil.kt @@ -18,7 +18,6 @@ fun createMockPublicKeyAssertionResponse(number: Int) = clientExtensionResults = ClientExtensionResults( credProps = CredPropsResult( rk = true, - authenticatorDisplayName = "mockAuthenticatorDisplayName-$number", ), ), response = AuthenticatorAssertionResponse( diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/PublicKeyAuthenticatorAttestationResponseUtil.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/PublicKeyAuthenticatorAttestationResponseUtil.kt index 934117a4628..e7821bd5368 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/PublicKeyAuthenticatorAttestationResponseUtil.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/model/PublicKeyAuthenticatorAttestationResponseUtil.kt @@ -18,7 +18,6 @@ fun createMockPublicKeyAttestationResponse(number: Int) = clientExtensionResults = ClientExtensionResults( credProps = CredPropsResult( rk = true, - authenticatorDisplayName = "mockDisplayName", ), ), response = AuthenticatorAttestationResponse( diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/util/PublicKeyCredentialAuthenticatorAssertionResponseExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/util/PublicKeyCredentialAuthenticatorAssertionResponseExtensionsTest.kt index d9ff79f66d6..113ab008043 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/util/PublicKeyCredentialAuthenticatorAssertionResponseExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/util/PublicKeyCredentialAuthenticatorAssertionResponseExtensionsTest.kt @@ -61,7 +61,6 @@ class PublicKeyCredentialAuthenticatorAssertionResponseExtensionsTest { number = 1, credProps = CredPropsResult( rk = true, - authenticatorDisplayName = null, ), ) val result = mockSdkResponse.toAndroidFido2PublicKeyCredential() @@ -74,7 +73,6 @@ class PublicKeyCredentialAuthenticatorAssertionResponseExtensionsTest { number = 1, credProps = CredPropsResult( rk = null, - authenticatorDisplayName = null, ), ) val result = mockSdkResponse.toAndroidFido2PublicKeyCredential() diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/util/PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/util/PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest.kt index 82f4861fec1..f0f06962e83 100644 --- a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/util/PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest.kt +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/datasource/sdk/util/PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest.kt @@ -60,7 +60,6 @@ class PublicKeyCredentialAuthenticatorAttestationResponseExtensionsTest { number = 1, credProps = CredPropsResult( rk = true, - authenticatorDisplayName = null, ), ) val result = mockSdkResponse.toAndroidAttestationResponse(callingPackageName = "") 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 9d2a3157085..345ba71bfc7 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 @@ -37,6 +37,7 @@ import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResul import com.x8bit.bitwarden.data.vault.manager.model.VaultStateEvent import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState import io.mockk.clearMocks import io.mockk.coEvery import io.mockk.coVerify @@ -760,15 +761,18 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = MOCK_PROFILE.toSdkParams(), email = MOCK_PROFILE.email, - privateKey = privateKey, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = userAutoUnlockKey, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -786,15 +790,18 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = MOCK_PROFILE.toSdkParams(), email = MOCK_PROFILE.email, - privateKey = privateKey, method = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = userAutoUnlockKey, ), - signingKey = null, - securityState = null, ), ) trustedDeviceManager.trustThisDeviceIfNecessary(userId = USER_ID) @@ -918,16 +925,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -951,12 +961,15 @@ class VaultLockManagerTest { ) val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -987,16 +1000,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) vaultSdkSource.initializeOrganizationCrypto( @@ -1022,16 +1038,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -1064,12 +1083,15 @@ class VaultLockManagerTest { } val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -1102,16 +1124,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) vaultSdkSource.initializeOrganizationCrypto( @@ -1137,16 +1162,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.AuthenticationError(error = error).asSuccess() @@ -1161,12 +1189,15 @@ class VaultLockManagerTest { ) val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -1187,16 +1218,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } @@ -1215,16 +1249,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -1246,12 +1283,15 @@ class VaultLockManagerTest { ) val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -1272,16 +1312,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } @@ -1307,16 +1350,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns error.asFailure() @@ -1330,12 +1376,15 @@ class VaultLockManagerTest { ) val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -1356,16 +1405,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } @@ -1384,16 +1436,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -1414,12 +1469,15 @@ class VaultLockManagerTest { ) val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -1440,16 +1498,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } @@ -1474,16 +1535,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -1504,12 +1568,15 @@ class VaultLockManagerTest { ) val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -1537,16 +1604,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } @@ -1646,16 +1716,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -1675,12 +1748,15 @@ class VaultLockManagerTest { mutableVaultTimeoutStateFlow.value = VaultTimeout.ThirtyMinutes val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -1703,16 +1779,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) vaultSdkSource.initializeOrganizationCrypto( @@ -1741,13 +1820,16 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = initUserCryptoMethod, - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -1771,12 +1853,15 @@ class VaultLockManagerTest { ) val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = initUserCryptoMethod, organizationKeys = organizationKeys, ) @@ -1804,13 +1889,16 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = USER_ID, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = USER_ID, kdfParams = kdf, email = email, - privateKey = privateKey, method = initUserCryptoMethod, - signingKey = null, - securityState = null, ), ) vaultSdkSource.initializeOrganizationCrypto( @@ -1852,16 +1940,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = userId, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } coAnswers { @@ -1870,12 +1961,15 @@ class VaultLockManagerTest { } vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MasterPasswordUnlockData( @@ -1905,16 +1999,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = userId, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } returns InitializeCryptoResult.Success.asSuccess() @@ -1926,12 +2023,15 @@ class VaultLockManagerTest { } returns true.asSuccess() val result = vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = email, kdf = kdf, - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, @@ -1944,16 +2044,19 @@ class VaultLockManagerTest { vaultSdkSource.initializeCrypto( userId = userId, request = InitUserCryptoRequest( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, kdfParams = kdf, email = email, - privateKey = privateKey, method = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = MOCK_MASTER_PASSWORD_UNLOCK_DATA, ), - signingKey = null, - securityState = null, ), ) } 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 964c51d09e9..686801d3e6f 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 @@ -56,6 +56,7 @@ import com.x8bit.bitwarden.data.vault.repository.model.ImportCredentialsResult import com.x8bit.bitwarden.data.vault.repository.model.SendData import com.x8bit.bitwarden.data.vault.repository.model.VaultData import com.x8bit.bitwarden.data.vault.repository.model.VaultUnlockResult +import com.x8bit.bitwarden.data.vault.repository.util.createWrappedAccountCryptographicState import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkCipher import com.x8bit.bitwarden.data.vault.repository.util.toSdkMasterPasswordUnlock import com.x8bit.bitwarden.ui.vault.feature.verificationcode.util.createVerificationCodeItem @@ -258,12 +259,15 @@ class VaultRepositoryTest { } coEvery { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = encryptedBytes.toString(Charsets.ISO_8859_1), ), @@ -281,12 +285,15 @@ class VaultRepositoryTest { assertEquals(VaultUnlockResult.Success, result) coVerify(exactly = 1) { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = encryptedBytes.toString(Charsets.ISO_8859_1), ), @@ -311,12 +318,15 @@ class VaultRepositoryTest { } coEvery { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = biometricsKey, ), @@ -334,12 +344,15 @@ class VaultRepositoryTest { assertEquals(VaultUnlockResult.Success, result) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = biometricsKey, ), @@ -368,12 +381,15 @@ class VaultRepositoryTest { fakeAuthDiskSource.userState = MOCK_USER_STATE coEvery { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = authenticatorSyncUnlockKey, ), @@ -395,12 +411,15 @@ class VaultRepositoryTest { assertEquals(VaultUnlockResult.Success, result) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = authenticatorSyncUnlockKey, ), @@ -420,12 +439,15 @@ class VaultRepositoryTest { val error = Throwable("Fail") coEvery { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = privateKey, + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = privateKey, - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = authenticatorSyncUnlockKey, ), @@ -447,12 +469,15 @@ class VaultRepositoryTest { assertEquals(VaultUnlockResult.InvalidStateError(error = error), result) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.DecryptedKey( decryptedUserKey = authenticatorSyncUnlockKey, ), @@ -531,12 +556,15 @@ class VaultRepositoryTest { ) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = "mockPassword-1", masterPasswordUnlock = MasterPasswordUnlockData( @@ -585,12 +613,15 @@ class VaultRepositoryTest { coEvery { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = masterPasswordUnlockData, @@ -606,12 +637,15 @@ class VaultRepositoryTest { assertEquals(VaultUnlockResult.Success, result) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = masterPassword, masterPasswordUnlock = masterPasswordUnlockData, @@ -647,7 +681,7 @@ class VaultRepositoryTest { assertTrue(result is VaultUnlockResult.InvalidStateError) coVerify(exactly = 0) { - vaultLockManager.unlockVault(any(), any(), any(), any(), any(), any(), any(), any()) + vaultLockManager.unlockVault(any(), any(), any(), any(), any(), any()) } } @@ -669,12 +703,15 @@ class VaultRepositoryTest { ) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = "mockPassword-1", masterPasswordUnlock = MasterPasswordUnlockData( @@ -762,12 +799,15 @@ class VaultRepositoryTest { ) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope( pin = "1234", pinProtectedUserKeyEnvelope = "mockKey-1", @@ -797,12 +837,15 @@ class VaultRepositoryTest { ) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.Pin( pin = "1234", pinProtectedUserKey = "mockKey-1", @@ -828,12 +871,15 @@ class VaultRepositoryTest { ) coVerify { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope( pin = "1234", pinProtectedUserKeyEnvelope = "mockKey-1", @@ -1364,12 +1410,14 @@ class VaultRepositoryTest { val userId = "userId" val fido2CredentialStore: Fido2CredentialStore = mockk() val relyingPartyId = "relyingPartyId" + val userHandle = "mockUserHandle" val expected: List = mockk() coEvery { vaultSdkSource.silentlyDiscoverCredentials( userId = userId, fido2CredentialStore = fido2CredentialStore, relyingPartyId = relyingPartyId, + userHandle = userHandle, ) } returns expected.asSuccess() @@ -1377,6 +1425,7 @@ class VaultRepositoryTest { userId = userId, fido2CredentialStore = fido2CredentialStore, relyingPartyId = relyingPartyId, + userHandle = userHandle, ) assertEquals(expected.asSuccess(), result) @@ -1386,6 +1435,7 @@ class VaultRepositoryTest { userId = userId, fido2CredentialStore = fido2CredentialStore, relyingPartyId = relyingPartyId, + userHandle = userHandle, ) } } @@ -1529,12 +1579,15 @@ class VaultRepositoryTest { // Master password unlock coEvery { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.MasterPasswordUnlock( password = mockMasterPassword, masterPasswordUnlock = MasterPasswordUnlockData( @@ -1550,12 +1603,15 @@ class VaultRepositoryTest { // PIN unlock coEvery { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.Pin( pin = mockPin, pinProtectedUserKey = "mockKey-1", @@ -1567,12 +1623,15 @@ class VaultRepositoryTest { // PIN ENVELOPE unlock coEvery { vaultLockManager.unlockVault( + accountCryptographicState = createWrappedAccountCryptographicState( + privateKey = "mockPrivateKey-1", + securityState = null, + signedPublicKey = null, + signingKey = null, + ), userId = userId, email = "email", kdf = MOCK_PROFILE.toSdkParams(), - privateKey = "mockPrivateKey-1", - signingKey = null, - securityState = null, initUserCryptoMethod = InitUserCryptoMethod.PinEnvelope( pin = mockPin, pinProtectedUserKeyEnvelope = "mockKey-1", diff --git a/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/util/WrappedAccountCryptographicStateExtensionsTest.kt b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/util/WrappedAccountCryptographicStateExtensionsTest.kt new file mode 100644 index 00000000000..75a4b07cf52 --- /dev/null +++ b/app/src/test/kotlin/com/x8bit/bitwarden/data/vault/repository/util/WrappedAccountCryptographicStateExtensionsTest.kt @@ -0,0 +1,111 @@ +package com.x8bit.bitwarden.data.vault.repository.util + +import com.bitwarden.core.WrappedAccountCryptographicState +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Test + +private const val PRIVATE_KEY = "test-private-key" +private const val SECURITY_STATE = "test-security-state" +private const val SIGNING_KEY = "test-signing-key" +private const val SIGNED_PUBLIC_KEY = "test-signed-public-key" + +class WrappedAccountCryptographicStateExtensionsTest { + + @Suppress("MaxLineLength") + @Test + fun `createWrappedAccountCryptographicState returns V2 when securityState, signedPublicKey and signingKey are non-null`() { + val result = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = SECURITY_STATE, + signedPublicKey = SIGNED_PUBLIC_KEY, + signingKey = SIGNING_KEY, + ) + + val v2State = result as WrappedAccountCryptographicState.V2 + assertEquals(PRIVATE_KEY, v2State.privateKey) + assertEquals(SECURITY_STATE, v2State.securityState) + assertEquals(SIGNED_PUBLIC_KEY, v2State.signedPublicKey) + assertEquals(SIGNING_KEY, v2State.signingKey) + } + + @Test + fun `createWrappedAccountCryptographicState returns V1 when securityState is null`() { + val result = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = null, + signedPublicKey = SIGNED_PUBLIC_KEY, + signingKey = SIGNING_KEY, + ) + + val v1State = result as WrappedAccountCryptographicState.V1 + assertEquals(PRIVATE_KEY, v1State.privateKey) + } + + @Test + fun `createWrappedAccountCryptographicState returns V1 when signedPublicKey is null`() { + val result = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = SECURITY_STATE, + signedPublicKey = null, + signingKey = SIGNING_KEY, + ) + + val v1State = result as WrappedAccountCryptographicState.V1 + assertEquals(PRIVATE_KEY, v1State.privateKey) + } + + @Test + fun `createWrappedAccountCryptographicState returns V1 when signingKey is null`() { + val result = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = SECURITY_STATE, + signedPublicKey = SIGNED_PUBLIC_KEY, + signingKey = null, + ) + + val v1State = result as WrappedAccountCryptographicState.V1 + assertEquals(PRIVATE_KEY, v1State.privateKey) + } + + @Suppress("MaxLineLength") + @Test + fun `createWrappedAccountCryptographicState returns V1 when both securityState and signedPublicKey are null`() { + val result = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = null, + signedPublicKey = null, + signingKey = SIGNING_KEY, + ) + + val v1State = result as WrappedAccountCryptographicState.V1 + assertEquals(PRIVATE_KEY, v1State.privateKey) + } + + @Suppress("MaxLineLength") + @Test + fun `createWrappedAccountCryptographicState returns V1 when both securityState and signingKey are null`() { + val result = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = null, + signedPublicKey = SIGNED_PUBLIC_KEY, + signingKey = null, + ) + + val v1State = result as WrappedAccountCryptographicState.V1 + assertEquals(PRIVATE_KEY, v1State.privateKey) + } + + @Suppress("MaxLineLength") + @Test + fun `createWrappedAccountCryptographicState returns V1 when both signedPublicKey and signingKey are null`() { + val result = createWrappedAccountCryptographicState( + privateKey = PRIVATE_KEY, + securityState = SECURITY_STATE, + signedPublicKey = null, + signingKey = null, + ) + + val v1State = result as WrappedAccountCryptographicState.V1 + assertEquals(PRIVATE_KEY, v1State.privateKey) + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e0a66b298f1..05663d581a4 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -30,7 +30,7 @@ androidxRoom = "2.8.4" androidxSecurityCrypto = "1.1.0" androidxSplash = "1.2.0" androidxWork = "2.11.0" -bitwardenSdk = "1.0.0-3958-7f09fd2f" +bitwardenSdk = "2.0.0-4254-6c954013" crashlytics = "3.0.6" detekt = "1.23.8" firebaseBom = "34.5.0" diff --git a/network/src/testFixtures/kotlin/com/bitwarden/network/model/AccountKeysJsonUtil.kt b/network/src/testFixtures/kotlin/com/bitwarden/network/model/AccountKeysJsonUtil.kt index 11b9c23762d..03e697fe60d 100644 --- a/network/src/testFixtures/kotlin/com/bitwarden/network/model/AccountKeysJsonUtil.kt +++ b/network/src/testFixtures/kotlin/com/bitwarden/network/model/AccountKeysJsonUtil.kt @@ -7,9 +7,9 @@ fun createMockAccountKeysJson( number: Int, ): AccountKeysJson = AccountKeysJson( - signatureKeyPair = createMockSignatureKeyPair(number = number), publicKeyEncryptionKeyPair = createMockPublicKeyEncryptionKeyPair(number = number), securityState = createMockSecurityState(number = number), + signatureKeyPair = createMockSignatureKeyPair(number = number), ) /** @@ -53,3 +53,18 @@ fun createMockSignatureKeyPair( wrappedSigningKey = wrappedSigningKey, verifyingKey = verifyingKey, ) + +/** + * Create a mock set of account keys with null nested fields for testing null-safety. + */ +fun createMockAccountKeysJsonWithNullFields( + number: Int, +): AccountKeysJson = + AccountKeysJson( + publicKeyEncryptionKeyPair = createMockPublicKeyEncryptionKeyPair( + number = number, + signedPublicKey = null, + ), + securityState = null, + signatureKeyPair = null, + )