Encrypt file WIP

This commit is contained in:
Benoit Marty 2019-07-02 09:53:17 +02:00 committed by Benoit Marty
parent 014d03893a
commit 0c2d3f36c3
3 changed files with 91 additions and 26 deletions

View File

@ -42,7 +42,7 @@ object MXEncryptedAttachments {
*/ */
data class EncryptionResult( data class EncryptionResult(
var encryptedFileInfo: EncryptedFileInfo, var encryptedFileInfo: EncryptedFileInfo,
var encryptedStream: InputStream var encryptedByteArray: ByteArray
) )


/*** /***
@ -112,7 +112,7 @@ object MXEncryptedAttachments {
hashes = mapOf("sha256" to base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT))!!), hashes = mapOf("sha256" to base64ToUnpaddedBase64(Base64.encodeToString(messageDigest.digest(), Base64.DEFAULT))!!),
v = "v2" v = "v2"
), ),
encryptedStream = ByteArrayInputStream(outStream.toByteArray()) encryptedByteArray = outStream.toByteArray()
) )


outStream.close() outStream.close()

View File

@ -25,13 +25,17 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.toContent import im.vector.matrix.android.api.session.events.model.toContent
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.internal.crypto.attachments.MXEncryptedAttachments
import im.vector.matrix.android.internal.crypto.model.rest.EncryptedFileInfo
import im.vector.matrix.android.internal.network.ProgressRequestBody import im.vector.matrix.android.internal.network.ProgressRequestBody
import im.vector.matrix.android.internal.session.room.send.SendEventWorker import im.vector.matrix.android.internal.session.room.send.SendEventWorker
import im.vector.matrix.android.internal.worker.SessionWorkerParams import im.vector.matrix.android.internal.worker.SessionWorkerParams
import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.WorkerParamsFactory
import im.vector.matrix.android.internal.worker.getSessionComponent import im.vector.matrix.android.internal.worker.getSessionComponent
import timber.log.Timber import timber.log.Timber
import java.io.ByteArrayInputStream
import java.io.File import java.io.File
import java.io.FileInputStream
import javax.inject.Inject import javax.inject.Inject




@ -42,7 +46,8 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
override val userId: String, override val userId: String,
val roomId: String, val roomId: String,
val event: Event, val event: Event,
val attachment: ContentAttachmentData val attachment: ContentAttachmentData,
val isRoomEncrypted: Boolean
) : SessionWorkerParams ) : SessionWorkerParams


@Inject lateinit var fileUploader: FileUploader @Inject lateinit var fileUploader: FileUploader
@ -58,17 +63,33 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
val eventId = params.event.eventId ?: return Result.success() val eventId = params.event.eventId ?: return Result.success()
val attachment = params.attachment val attachment = params.attachment


val isRoomEncrypted = params.isRoomEncrypted


val thumbnailData = ThumbnailExtractor.extractThumbnail(params.attachment) val thumbnailData = ThumbnailExtractor.extractThumbnail(params.attachment)
val attachmentFile = createAttachmentFile(attachment) ?: return Result.failure() val attachmentFile = createAttachmentFile(attachment) ?: return Result.failure()
var uploadedThumbnailUrl: String? = null var uploadedThumbnailUrl: String? = null
var uploadedThumbnailEncryptedFileInfo: EncryptedFileInfo? = null


if (thumbnailData != null) { if (thumbnailData != null) {
fileUploader if (isRoomEncrypted) {
.uploadByteArray(thumbnailData.bytes, "thumb_${attachment.name}", thumbnailData.mimeType) Timber.v("Encrypt thumbnail")
val encryptionResult = MXEncryptedAttachments.encryptAttachment(ByteArrayInputStream(thumbnailData.bytes), thumbnailData.mimeType)
?: return Result.failure()

uploadedThumbnailEncryptedFileInfo = encryptionResult.encryptedFileInfo

fileUploader
.uploadByteArray(encryptionResult.encryptedByteArray, "thumb_${attachment.name}", thumbnailData.mimeType)
} else {
fileUploader
.uploadByteArray(thumbnailData.bytes, "thumb_${attachment.name}", thumbnailData.mimeType)
}
.fold( .fold(
{ Timber.e(it) }, { Timber.e(it) },
{ uploadedThumbnailUrl = it.contentUri } { uploadedThumbnailUrl = it.contentUri }
) )

} }


val progressListener = object : ProgressRequestBody.Listener { val progressListener = object : ProgressRequestBody.Listener {
@ -76,12 +97,28 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :
contentUploadStateTracker.setProgress(eventId, current, total) contentUploadStateTracker.setProgress(eventId, current, total)
} }
} }
return fileUploader
.uploadFile(attachmentFile, attachment.name, attachment.mimeType, progressListener) var uploadedFileEncryptedFileInfo: EncryptedFileInfo? = null

return if (isRoomEncrypted) {
Timber.v("Encrypt file")

val encryptionResult = MXEncryptedAttachments.encryptAttachment(FileInputStream(attachmentFile), attachment.mimeType)
?: return Result.failure()

uploadedFileEncryptedFileInfo = encryptionResult.encryptedFileInfo

fileUploader
.uploadByteArray(encryptionResult.encryptedByteArray, attachment.name, attachment.mimeType, progressListener)
} else {
fileUploader
.uploadFile(attachmentFile, attachment.name, attachment.mimeType, progressListener)
}
.fold( .fold(
{ handleFailure(params) }, { handleFailure(params) },
{ handleSuccess(params, it.contentUri, uploadedThumbnailUrl) } { handleSuccess(params, it.contentUri, uploadedFileEncryptedFileInfo, uploadedThumbnailUrl, uploadedThumbnailEncryptedFileInfo) }
) )

} }


private fun createAttachmentFile(attachment: ContentAttachmentData): File? { private fun createAttachmentFile(attachment: ContentAttachmentData): File? {
@ -100,39 +137,67 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) :


private fun handleSuccess(params: Params, private fun handleSuccess(params: Params,
attachmentUrl: String, attachmentUrl: String,
thumbnailUrl: String?): Result { encryptedFileInfo: EncryptedFileInfo?,
thumbnailUrl: String?,
thumbnailEncryptedFileInfo: EncryptedFileInfo?): Result {
contentUploadStateTracker.setSuccess(params.event.eventId!!) contentUploadStateTracker.setSuccess(params.event.eventId!!)
val event = updateEvent(params.event, attachmentUrl, thumbnailUrl) val event = updateEvent(params.event, attachmentUrl, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
val sendParams = SendEventWorker.Params(params.userId, params.roomId, event) val sendParams = SendEventWorker.Params(params.userId, params.roomId, event)
return Result.success(WorkerParamsFactory.toData(sendParams)) return Result.success(WorkerParamsFactory.toData(sendParams))
} }


private fun updateEvent(event: Event, url: String, thumbnailUrl: String? = null): Event { private fun updateEvent(event: Event,
url: String,
encryptedFileInfo: EncryptedFileInfo?,
thumbnailUrl: String? = null,
thumbnailEncryptedFileInfo: EncryptedFileInfo?): Event {
val messageContent: MessageContent = event.content.toModel() ?: return event val messageContent: MessageContent = event.content.toModel() ?: return event
val updatedContent = when (messageContent) { val updatedContent = when (messageContent) {
is MessageImageContent -> messageContent.update(url) is MessageImageContent -> messageContent.update(url, encryptedFileInfo)
is MessageVideoContent -> messageContent.update(url, thumbnailUrl) is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo)
is MessageFileContent -> messageContent.update(url) is MessageFileContent -> messageContent.update(url, encryptedFileInfo)
is MessageAudioContent -> messageContent.update(url) is MessageAudioContent -> messageContent.update(url, encryptedFileInfo)
else -> messageContent else -> messageContent
} }
return event.copy(content = updatedContent.toContent()) return event.copy(content = updatedContent.toContent())
} }


private fun MessageImageContent.update(url: String): MessageImageContent { private fun MessageImageContent.update(url: String,
return copy(url = url) encryptedFileInfo: EncryptedFileInfo?): MessageImageContent {
return copy(
url = url,
encryptedFileInfo = encryptedFileInfo
)
} }


private fun MessageVideoContent.update(url: String, thumbnailUrl: String?): MessageVideoContent { private fun MessageVideoContent.update(url: String,
return copy(url = url, videoInfo = videoInfo?.copy(thumbnailUrl = thumbnailUrl)) encryptedFileInfo: EncryptedFileInfo?,
thumbnailUrl: String?,
thumbnailEncryptedFileInfo: EncryptedFileInfo?): MessageVideoContent {
return copy(
url = url,
encryptedFileInfo = encryptedFileInfo,
videoInfo = videoInfo?.copy(
thumbnailUrl = thumbnailUrl,
thumbnailFile = thumbnailEncryptedFileInfo
)
)
} }


private fun MessageFileContent.update(url: String): MessageFileContent { private fun MessageFileContent.update(url: String,
return copy(url = url) encryptedFileInfo: EncryptedFileInfo?): MessageFileContent {
return copy(
url = url,
encryptedFileInfo = encryptedFileInfo
)
} }


private fun MessageAudioContent.update(url: String): MessageAudioContent { private fun MessageAudioContent.update(url: String,
return copy(url = url) encryptedFileInfo: EncryptedFileInfo?): MessageAudioContent {
return copy(
url = url,
encryptedFileInfo = encryptedFileInfo
)
} }


} }

View File

@ -101,7 +101,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also { val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also {
saveLocalEcho(it) saveLocalEcho(it)
} }
val uploadWork = createUploadMediaWork(event, attachment) val uploadWork = createUploadMediaWork(event, attachment, cryptoService.isRoomEncrypted(roomId))
val sendWork = createSendEventWork(event) val sendWork = createSendEventWork(event)


WorkManager.getInstance(context) WorkManager.getInstance(context)
@ -148,8 +148,8 @@ internal class DefaultSendService @Inject constructor(private val context: Conte
return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData) return TimelineSendEventWorkCommon.createWork<RedactEventWorker>(redactWorkData)
} }


private fun createUploadMediaWork(event: Event, attachment: ContentAttachmentData): OneTimeWorkRequest { private fun createUploadMediaWork(event: Event, attachment: ContentAttachmentData, isRoomEncrypted: Boolean): OneTimeWorkRequest {
val uploadMediaWorkerParams = UploadContentWorker.Params(credentials.userId, roomId, event, attachment) val uploadMediaWorkerParams = UploadContentWorker.Params(credentials.userId, roomId, event, attachment, isRoomEncrypted)
val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams) val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams)


return OneTimeWorkRequestBuilder<UploadContentWorker>() return OneTimeWorkRequestBuilder<UploadContentWorker>()