From 0c2d3f36c3c323db18e4c87ca0050f933db84910 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Tue, 2 Jul 2019 09:53:17 +0200 Subject: [PATCH] Encrypt file WIP --- .../attachments/MXEncryptedAttachments.kt | 4 +- .../session/content/UploadContentWorker.kt | 107 ++++++++++++++---- .../session/room/send/DefaultSendService.kt | 6 +- 3 files changed, 91 insertions(+), 26 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MXEncryptedAttachments.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MXEncryptedAttachments.kt index 033ce1f0..7fe82421 100755 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MXEncryptedAttachments.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/attachments/MXEncryptedAttachments.kt @@ -42,7 +42,7 @@ object MXEncryptedAttachments { */ data class EncryptionResult( 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))!!), v = "v2" ), - encryptedStream = ByteArrayInputStream(outStream.toByteArray()) + encryptedByteArray = outStream.toByteArray() ) outStream.close() diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt index b459d288..68dac276 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt @@ -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.toModel 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.session.room.send.SendEventWorker import im.vector.matrix.android.internal.worker.SessionWorkerParams import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.getSessionComponent import timber.log.Timber +import java.io.ByteArrayInputStream import java.io.File +import java.io.FileInputStream import javax.inject.Inject @@ -42,7 +46,8 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : override val userId: String, val roomId: String, val event: Event, - val attachment: ContentAttachmentData + val attachment: ContentAttachmentData, + val isRoomEncrypted: Boolean ) : SessionWorkerParams @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 attachment = params.attachment + val isRoomEncrypted = params.isRoomEncrypted + + val thumbnailData = ThumbnailExtractor.extractThumbnail(params.attachment) val attachmentFile = createAttachmentFile(attachment) ?: return Result.failure() var uploadedThumbnailUrl: String? = null + var uploadedThumbnailEncryptedFileInfo: EncryptedFileInfo? = null if (thumbnailData != null) { - fileUploader - .uploadByteArray(thumbnailData.bytes, "thumb_${attachment.name}", thumbnailData.mimeType) + if (isRoomEncrypted) { + 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( { Timber.e(it) }, { uploadedThumbnailUrl = it.contentUri } ) + } val progressListener = object : ProgressRequestBody.Listener { @@ -76,12 +97,28 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : 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( { handleFailure(params) }, - { handleSuccess(params, it.contentUri, uploadedThumbnailUrl) } + { handleSuccess(params, it.contentUri, uploadedFileEncryptedFileInfo, uploadedThumbnailUrl, uploadedThumbnailEncryptedFileInfo) } ) + } private fun createAttachmentFile(attachment: ContentAttachmentData): File? { @@ -100,39 +137,67 @@ internal class UploadContentWorker(context: Context, params: WorkerParameters) : private fun handleSuccess(params: Params, attachmentUrl: String, - thumbnailUrl: String?): Result { + encryptedFileInfo: EncryptedFileInfo?, + thumbnailUrl: String?, + thumbnailEncryptedFileInfo: EncryptedFileInfo?): Result { 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) 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 updatedContent = when (messageContent) { - is MessageImageContent -> messageContent.update(url) - is MessageVideoContent -> messageContent.update(url, thumbnailUrl) - is MessageFileContent -> messageContent.update(url) - is MessageAudioContent -> messageContent.update(url) + is MessageImageContent -> messageContent.update(url, encryptedFileInfo) + is MessageVideoContent -> messageContent.update(url, encryptedFileInfo, thumbnailUrl, thumbnailEncryptedFileInfo) + is MessageFileContent -> messageContent.update(url, encryptedFileInfo) + is MessageAudioContent -> messageContent.update(url, encryptedFileInfo) else -> messageContent } return event.copy(content = updatedContent.toContent()) } - private fun MessageImageContent.update(url: String): MessageImageContent { - return copy(url = url) + private fun MessageImageContent.update(url: String, + encryptedFileInfo: EncryptedFileInfo?): MessageImageContent { + return copy( + url = url, + encryptedFileInfo = encryptedFileInfo + ) } - private fun MessageVideoContent.update(url: String, thumbnailUrl: String?): MessageVideoContent { - return copy(url = url, videoInfo = videoInfo?.copy(thumbnailUrl = thumbnailUrl)) + private fun MessageVideoContent.update(url: String, + 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 { - return copy(url = url) + private fun MessageFileContent.update(url: String, + encryptedFileInfo: EncryptedFileInfo?): MessageFileContent { + return copy( + url = url, + encryptedFileInfo = encryptedFileInfo + ) } - private fun MessageAudioContent.update(url: String): MessageAudioContent { - return copy(url = url) + private fun MessageAudioContent.update(url: String, + encryptedFileInfo: EncryptedFileInfo?): MessageAudioContent { + return copy( + url = url, + encryptedFileInfo = encryptedFileInfo + ) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index fb6f1be0..eaf0c414 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -101,7 +101,7 @@ internal class DefaultSendService @Inject constructor(private val context: Conte val event = localEchoEventFactory.createMediaEvent(roomId, attachment).also { saveLocalEcho(it) } - val uploadWork = createUploadMediaWork(event, attachment) + val uploadWork = createUploadMediaWork(event, attachment, cryptoService.isRoomEncrypted(roomId)) val sendWork = createSendEventWork(event) WorkManager.getInstance(context) @@ -148,8 +148,8 @@ internal class DefaultSendService @Inject constructor(private val context: Conte return TimelineSendEventWorkCommon.createWork(redactWorkData) } - private fun createUploadMediaWork(event: Event, attachment: ContentAttachmentData): OneTimeWorkRequest { - val uploadMediaWorkerParams = UploadContentWorker.Params(credentials.userId, roomId, event, attachment) + private fun createUploadMediaWork(event: Event, attachment: ContentAttachmentData, isRoomEncrypted: Boolean): OneTimeWorkRequest { + val uploadMediaWorkerParams = UploadContentWorker.Params(credentials.userId, roomId, event, attachment, isRoomEncrypted) val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams) return OneTimeWorkRequestBuilder()