BIT-1117: Decrypt sends (#254)

This commit is contained in:
Ramsey Smith
2023-11-17 12:52:56 -07:00
committed by Álison Fernandes
parent db5c57664f
commit ba5509511f
12 changed files with 582 additions and 25 deletions

View File

@@ -6,6 +6,8 @@ import com.bitwarden.core.CipherView
import com.bitwarden.core.Folder
import com.bitwarden.core.FolderView
import com.bitwarden.core.InitCryptoRequest
import com.bitwarden.core.Send
import com.bitwarden.core.SendView
import com.x8bit.bitwarden.data.vault.datasource.sdk.model.InitializeCryptoResult
/**
@@ -34,6 +36,16 @@ interface VaultSdkSource {
*/
suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherView>>
/**
* Decrypts a [Send] returning a [SendView] wrapped in a [Result].
*/
suspend fun decryptSend(send: Send): Result<SendView>
/**
* Decrypts a list of [Send]s returning a list of [SendView] wrapped in a [Result].
*/
suspend fun decryptSendList(sendList: List<Send>): Result<List<SendView>>
/**
* Decrypts a [Folder] returning a [FolderView] wrapped in a [Result].
*/

View File

@@ -6,6 +6,8 @@ import com.bitwarden.core.CipherView
import com.bitwarden.core.Folder
import com.bitwarden.core.FolderView
import com.bitwarden.core.InitCryptoRequest
import com.bitwarden.core.Send
import com.bitwarden.core.SendView
import com.bitwarden.sdk.BitwardenException
import com.bitwarden.sdk.ClientCrypto
import com.bitwarden.sdk.ClientVault
@@ -43,6 +45,12 @@ class VaultSdkSourceImpl(
override suspend fun decryptCipherList(cipherList: List<Cipher>): Result<List<CipherView>> =
runCatching { cipherList.map { clientVault.ciphers().decrypt(it) } }
override suspend fun decryptSend(send: Send): Result<SendView> =
runCatching { clientVault.sends().decrypt(send) }
override suspend fun decryptSendList(sendList: List<Send>): Result<List<SendView>> =
runCatching { sendList.map { clientVault.sends().decrypt(it) } }
override suspend fun decryptFolder(folder: Folder): Result<FolderView> =
runCatching { clientVault.folders().decrypt(folder) }

View File

@@ -1,6 +1,7 @@
package com.x8bit.bitwarden.data.vault.repository
import com.x8bit.bitwarden.data.platform.repository.model.DataState
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 kotlinx.coroutines.flow.StateFlow
@@ -16,9 +17,14 @@ interface VaultRepository {
val vaultDataStateFlow: StateFlow<DataState<VaultData>>
/**
* Clear the in memory vault data.
* Flow that represents the current send data.
*/
fun clearVaultData()
val sendDataStateFlow: StateFlow<DataState<SendData>>
/**
* Clear any previously unlocked, in-memory data (vault, send, etc).
*/
fun clearUnlockedData()
/**
* Attempt to sync the vault data.

View File

@@ -10,10 +10,12 @@ import com.x8bit.bitwarden.data.platform.util.flatMap
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import com.x8bit.bitwarden.data.vault.datasource.network.service.SyncService
import com.x8bit.bitwarden.data.vault.datasource.sdk.VaultSdkSource
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.toEncryptedSdkCipherList
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkFolderList
import com.x8bit.bitwarden.data.vault.repository.util.toEncryptedSdkSendList
import com.x8bit.bitwarden.data.vault.repository.util.toVaultUnlockResult
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
@@ -46,11 +48,18 @@ class VaultRepositoryImpl constructor(
private val vaultDataMutableStateFlow =
MutableStateFlow<DataState<VaultData>>(DataState.Loading)
private val sendDataMutableStateFlow =
MutableStateFlow<DataState<SendData>>(DataState.Loading)
override val vaultDataStateFlow: StateFlow<DataState<VaultData>>
get() = vaultDataMutableStateFlow.asStateFlow()
override fun clearVaultData() {
override val sendDataStateFlow: StateFlow<DataState<SendData>>
get() = sendDataMutableStateFlow.asStateFlow()
override fun clearUnlockedData() {
vaultDataMutableStateFlow.update { DataState.Loading }
sendDataMutableStateFlow.update { DataState.Loading }
}
override fun sync() {
@@ -60,6 +69,11 @@ class VaultRepositoryImpl constructor(
DataState.Pending(data = data)
}
}
sendDataMutableStateFlow.value.data?.let { data ->
sendDataMutableStateFlow.update {
DataState.Pending(data = data)
}
}
syncJob = scope.launch {
syncService
.sync()
@@ -69,22 +83,19 @@ class VaultRepositoryImpl constructor(
userKey = syncResponse.profile?.key,
privateKey = syncResponse.profile?.privateKey,
)
decryptSyncResponseAndUpdateVaultDataState(
syncResponse = syncResponse,
)
decryptSyncResponseAndUpdateVaultDataState(syncResponse = syncResponse)
decryptSendsAndUpdateSendDataState(sendList = syncResponse.sends)
},
onFailure = { throwable ->
vaultDataMutableStateFlow.update {
if (throwable.isNoConnectionError()) {
DataState.NoNetwork(
data = it.data,
)
} else {
DataState.Error(
error = throwable,
data = it.data,
)
}
vaultDataMutableStateFlow.update { currentState ->
throwable.toNetworkOrErrorState(
data = currentState.data,
)
}
sendDataMutableStateFlow.update { currentState ->
throwable.toNetworkOrErrorState(
data = currentState.data,
)
}
},
)
@@ -148,16 +159,34 @@ class VaultRepositoryImpl constructor(
)
}
private suspend fun decryptSendsAndUpdateSendDataState(sendList: List<SyncResponseJson.Send>?) {
val newState = vaultSdkSource
.decryptSendList(
sendList = sendList
.orEmpty()
.toEncryptedSdkSendList(),
)
.fold(
onSuccess = { DataState.Loaded(data = SendData(sendViewList = it)) },
onFailure = { DataState.Error(error = it) },
)
sendDataMutableStateFlow.update { newState }
}
private suspend fun decryptSyncResponseAndUpdateVaultDataState(syncResponse: SyncResponseJson) {
val newState = vaultSdkSource
.decryptCipherList(
cipherList = (syncResponse.ciphers ?: emptyList())
cipherList = syncResponse
.ciphers
.orEmpty()
.toEncryptedSdkCipherList(),
)
.flatMap { decryptedCipherList ->
vaultSdkSource
.decryptFolderList(
folderList = (syncResponse.folders ?: emptyList())
folderList = syncResponse
.folders
.orEmpty()
.toEncryptedSdkFolderList(),
)
.map { decryptedFolderList ->
@@ -178,3 +207,13 @@ class VaultRepositoryImpl constructor(
vaultDataMutableStateFlow.update { newState }
}
}
private fun <T> Throwable.toNetworkOrErrorState(data: T?): DataState<T> =
if (isNoConnectionError()) {
DataState.NoNetwork(data = data)
} else {
DataState.Error(
error = this,
data = data,
)
}

View File

@@ -0,0 +1,12 @@
package com.x8bit.bitwarden.data.vault.repository.model
import com.bitwarden.core.SendView
/**
* Represents decrypted send data.
*
* @param sendViewList List of decrypted sends.
*/
data class SendData(
val sendViewList: List<SendView>,
)

View File

@@ -0,0 +1,72 @@
package com.x8bit.bitwarden.data.vault.repository.util
import com.bitwarden.core.Send
import com.bitwarden.core.SendFile
import com.bitwarden.core.SendText
import com.bitwarden.core.SendType
import com.x8bit.bitwarden.data.vault.datasource.network.model.SendTypeJson
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
import java.time.ZoneOffset
/**
* Converts a list of [SyncResponseJson.Send] objects to a list of corresponding
* Bitwarden SDK [Send] objects.
*/
fun List<SyncResponseJson.Send>.toEncryptedSdkSendList(): List<Send> =
map { it.toEncryptedSdkSend() }
/**
* Converts a [SyncResponseJson.Send] object to a corresponding
* Bitwarden SDK [Send] object.
*/
fun SyncResponseJson.Send.toEncryptedSdkSend(): Send =
Send(
id = id,
accessId = accessId.toString(),
name = name.toString(),
notes = notes,
key = key.toString(),
password = password,
type = type.toSdkSendType(),
file = file.toEncryptedSdkFile(),
text = text.toEncryptedSdkText(),
maxAccessCount = maxAccessCount?.toUInt(),
accessCount = accessCount.toUInt(),
disabled = isDisabled,
hideEmail = shouldHideEmail,
revisionDate = revisionDate.toInstant(ZoneOffset.UTC),
deletionDate = deletionDate.toInstant(ZoneOffset.UTC),
expirationDate = expirationDate?.toInstant(ZoneOffset.UTC),
)
/**
* Converts a [SyncResponseJson.Send.Text] object to a corresponding
* Bitwarden SDK [SendText] object.
*/
private fun SyncResponseJson.Send.Text.toEncryptedSdkText(): SendText =
SendText(
text = text,
hidden = isHidden,
)
/**
* Converts a [SyncResponseJson.Send.File] objects to a corresponding
* Bitwarden SDK [SendFile] object.
*/
private fun SyncResponseJson.Send.File.toEncryptedSdkFile(): SendFile =
SendFile(
id = id.toString(),
fileName = fileName.toString(),
size = size.toString(),
sizeName = sizeName.toString(),
)
/**
* Converts a [SendTypeJson] objects to a corresponding
* Bitwarden SDK [SendType].
*/
private fun SendTypeJson.toSdkSendType(): SendType =
when (this) {
SendTypeJson.TEXT -> SendType.TEXT
SendTypeJson.FILE -> SendType.FILE
}