diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSource.kt index 445bf5076..b12aca42c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSource.kt @@ -1,6 +1,7 @@ package com.x8bit.bitwarden.data.auth.datasource.sdk import com.bitwarden.core.AuthRequestResponse +import com.bitwarden.core.KeyConnectorResponse import com.bitwarden.core.MasterPasswordPolicyOptions import com.bitwarden.core.RegisterKeyResponse import com.bitwarden.core.RegisterTdeKeyResponse @@ -37,6 +38,11 @@ interface AuthSdkSource { purpose: HashPurpose, ): Result + /** + * Creates a set of encryption key information for use with a key connector. + */ + suspend fun makeKeyConnectorKeys(): Result + /** * Creates a set of encryption key information for registration. */ diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSourceImpl.kt index bbdfcb80c..c78d0cbd2 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSourceImpl.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.datasource.sdk import com.bitwarden.core.AuthRequestResponse import com.bitwarden.core.FingerprintRequest +import com.bitwarden.core.KeyConnectorResponse import com.bitwarden.core.MasterPasswordPolicyOptions import com.bitwarden.core.RegisterKeyResponse import com.bitwarden.core.RegisterTdeKeyResponse @@ -63,6 +64,13 @@ class AuthSdkSourceImpl( ) } + override suspend fun makeKeyConnectorKeys(): Result = + runCatchingWithLogs { + getClient() + .auth() + .makeKeyConnectorKeys() + } + override suspend fun makeRegisterKeys( email: String, password: String, diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt index c751f84b4..bc0cc3230 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSource.kt @@ -6,6 +6,7 @@ import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitUserCryptoMethod import com.bitwarden.core.InitUserCryptoRequest import com.bitwarden.core.UpdatePasswordResponse +import com.bitwarden.crypto.Kdf import com.bitwarden.crypto.TrustDeviceResponse import com.bitwarden.exporters.ExportFormat import com.bitwarden.fido.Fido2CredentialAutofillView @@ -52,6 +53,22 @@ interface VaultSdkSource { */ suspend fun getTrustDevice(userId: String): Result + /** + * Derives a "key connector" key from the given information for the given `userId. This can be + * used to later unlock their vault via a call to [initializeCrypto] with + * [InitUserCryptoMethod.KeyConnector]. + * + * This should only be called after a successful call to [initializeCrypto] for the associated + * user. + */ + suspend fun deriveKeyConnector( + userId: String, + userKeyEncrypted: String, + email: String, + password: String, + kdf: Kdf, + ): Result + /** * Derives a "pin key" from the given [pin] for the given [userId]. This can be used to later * unlock their vault via a call to [initializeCrypto] with [InitUserCryptoMethod.Pin]. diff --git a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt index ca7e67874..2c33c7450 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceImpl.kt @@ -1,10 +1,12 @@ package com.x8bit.bitwarden.data.vault.datasource.sdk import com.bitwarden.core.DateTime +import com.bitwarden.core.DeriveKeyConnectorRequest import com.bitwarden.core.DerivePinKeyResponse import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitUserCryptoRequest import com.bitwarden.core.UpdatePasswordResponse +import com.bitwarden.crypto.Kdf import com.bitwarden.crypto.TrustDeviceResponse import com.bitwarden.exporters.ExportFormat import com.bitwarden.fido.Fido2CredentialAutofillView @@ -67,6 +69,26 @@ class VaultSdkSourceImpl( .trustDevice() } + override suspend fun deriveKeyConnector( + userId: String, + userKeyEncrypted: String, + email: String, + password: String, + kdf: Kdf, + ): Result = + runCatchingWithLogs { + getClient(userId = userId) + .crypto() + .deriveKeyConnector( + request = DeriveKeyConnectorRequest( + userKeyEncrypted = userKeyEncrypted, + password = password, + kdf = kdf, + email = email, + ), + ) + } + override suspend fun derivePinKey( userId: String, pin: String, diff --git a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSourceTest.kt index f17a435ee..299c8cf37 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/auth/datasource/sdk/AuthSdkSourceTest.kt @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.datasource.sdk import com.bitwarden.core.AuthRequestResponse import com.bitwarden.core.FingerprintRequest +import com.bitwarden.core.KeyConnectorResponse import com.bitwarden.core.MasterPasswordPolicyOptions import com.bitwarden.core.RegisterKeyResponse import com.bitwarden.core.RegisterTdeKeyResponse @@ -124,6 +125,20 @@ class AuthSdkSourceTest { } } + @Test + fun `makeKeyConnectorKeys should call SDK and return a Result with the correct data`() = + runBlocking { + val expectedResult = mockk() + coEvery { clientAuth.makeKeyConnectorKeys() } returns expectedResult + + val result = authSkdSource.makeKeyConnectorKeys() + + assertEquals(expectedResult.asSuccess(), result) + coVerify(exactly = 1) { + clientAuth.makeKeyConnectorKeys() + } + } + @Test fun `makeRegisterKeys should call SDK and return a Result with the correct data`() = runBlocking { diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt index 628c19c71..73edcf85d 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/VaultSdkSourceTest.kt @@ -1,10 +1,12 @@ package com.x8bit.bitwarden.data.vault.datasource.sdk import com.bitwarden.core.DateTime +import com.bitwarden.core.DeriveKeyConnectorRequest import com.bitwarden.core.DerivePinKeyResponse import com.bitwarden.core.InitOrgCryptoRequest import com.bitwarden.core.InitUserCryptoRequest import com.bitwarden.core.UpdatePasswordResponse +import com.bitwarden.crypto.Kdf import com.bitwarden.crypto.TrustDeviceResponse import com.bitwarden.exporters.ExportFormat import com.bitwarden.fido.ClientData @@ -145,6 +147,46 @@ class VaultSdkSourceTest { } } + @Test + fun `deriveKeyConnector should call SDK and return a Result with the correct data`() = + runBlocking { + val userId = "userId" + val userKeyEncrypted = "userKeyEncrypted" + val email = "email" + val password = "password" + val expectedResult = "expectedResult" + val kdf = mockk() + coEvery { + clientCrypto.deriveKeyConnector( + request = DeriveKeyConnectorRequest( + userKeyEncrypted = userKeyEncrypted, + email = email, + password = password, + kdf = kdf, + ), + ) + } returns expectedResult + val result = vaultSdkSource.deriveKeyConnector( + userId = userId, + userKeyEncrypted = userKeyEncrypted, + email = email, + password = password, + kdf = kdf, + ) + assertEquals(expectedResult.asSuccess(), result) + coVerify(exactly = 1) { + sdkClientManager.getOrCreateClient(userId = userId) + clientCrypto.deriveKeyConnector( + request = DeriveKeyConnectorRequest( + userKeyEncrypted = userKeyEncrypted, + email = email, + password = password, + kdf = kdf, + ), + ) + } + } + @Test fun `derivePinKey should call SDK and return a Result with the correct data`() = runBlocking { val userId = "userId" diff --git a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherListViewUtil.kt b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherListViewUtil.kt index ae59d1671..e9b165599 100644 --- a/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherListViewUtil.kt +++ b/app/src/test/java/com/x8bit/bitwarden/data/vault/datasource/sdk/model/CipherListViewUtil.kt @@ -1,8 +1,8 @@ package com.x8bit.bitwarden.data.vault.datasource.sdk.model import com.bitwarden.vault.CipherListView +import com.bitwarden.vault.CipherListViewType import com.bitwarden.vault.CipherRepromptType -import com.bitwarden.vault.CipherType import java.time.ZonedDateTime /** @@ -15,7 +15,10 @@ fun createMockCipherListView(number: Int): CipherListView = folderId = "mockFolderId-$number", collectionIds = listOf("mockCollectionId-$number"), name = "mockName-$number", - type = CipherType.LOGIN, + type = CipherListViewType.Login( + hasFido2 = false, + totp = null, + ), creationDate = ZonedDateTime .parse("2023-10-27T12:00:00Z") .toInstant(), @@ -30,5 +33,6 @@ fun createMockCipherListView(number: Int): CipherListView = reprompt = CipherRepromptType.NONE, edit = false, viewPassword = false, - subTitle = "", + subTitle = "mockSubTitle-$number", + key = "mockKey-$number", ) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 25475ff9f..11f6d9367 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -24,7 +24,7 @@ androidxSplash = "1.1.0-rc01" androidXAppCompat = "1.7.0" androdixAutofill = "1.1.0" androidxWork = "2.9.1" -bitwardenSdk = "0.5.0-20240716.152920-144" +bitwardenSdk = "0.5.0-20240819.160739-177" crashlytics = "3.0.2" detekt = "1.23.6" firebaseBom = "33.1.2"