From 3c2a22655584b2a05db33dc8870a9613c9e68781 Mon Sep 17 00:00:00 2001 From: Adam Brown Date: Tue, 20 Sep 2022 20:54:35 +0100 Subject: [PATCH] extracting a dedicated media decrypter --- .../dapk/st/messenger/DecryptingFetcher.kt | 43 ++--------------- .../app/dapk/st/messenger/MediaDecrypter.kt | 47 +++++++++++++++++++ .../message/internal/SendMessageUseCase.kt | 39 +++++++++------ 3 files changed, 76 insertions(+), 53 deletions(-) create mode 100644 features/messenger/src/main/kotlin/app/dapk/st/messenger/MediaDecrypter.kt diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/DecryptingFetcher.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/DecryptingFetcher.kt index 0c89d90..4f27103 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/DecryptingFetcher.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/DecryptingFetcher.kt @@ -1,7 +1,6 @@ package app.dapk.st.messenger import android.content.Context -import android.util.Base64 import app.dapk.st.matrix.sync.RoomEvent import coil.ImageLoader import coil.decode.DataSource @@ -14,15 +13,6 @@ import okhttp3.OkHttpClient import okhttp3.Request import okhttp3.Response import okio.Buffer -import java.security.MessageDigest -import javax.crypto.Cipher -import javax.crypto.spec.IvParameterSpec -import javax.crypto.spec.SecretKeySpec - -private const val CRYPTO_BUFFER_SIZE = 32 * 1024 -private const val CIPHER_ALGORITHM = "AES/CTR/NoPadding" -private const val SECRET_KEY_SPEC_ALGORITHM = "AES" -private const val MESSAGE_DIGEST_ALGORITHM = "SHA-256" class DecryptingFetcherFactory(private val context: Context) : Fetcher.Factory { override fun create(data: RoomEvent.Image, options: Options, imageLoader: ImageLoader): Fetcher { @@ -34,6 +24,8 @@ private val http = OkHttpClient() class DecryptingFetcher(private val data: RoomEvent.Image, private val context: Context) : Fetcher { + private val mediaDecrypter = MediaDecrypter() + override suspend fun fetch(): FetchResult { val response = http.newCall(Request.Builder().url(data.imageMeta.url).build()).execute() val outputStream = when { @@ -44,32 +36,7 @@ class DecryptingFetcher(private val data: RoomEvent.Image, private val context: } private fun handleEncrypted(response: Response, keys: RoomEvent.Image.ImageMeta.Keys): Buffer { - val key = Base64.decode(keys.k.replace('-', '+').replace('_', '/'), Base64.DEFAULT) - val initVectorBytes = Base64.decode(keys.iv, Base64.DEFAULT) - - val decryptCipher = Cipher.getInstance(CIPHER_ALGORITHM) - val secretKeySpec = SecretKeySpec(key, SECRET_KEY_SPEC_ALGORITHM) - val ivParameterSpec = IvParameterSpec(initVectorBytes) - decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec) - - val messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM) - - var read: Int - val d = ByteArray(CRYPTO_BUFFER_SIZE) - var decodedBytes: ByteArray - - val outputStream = Buffer() - response.body?.let { - it.byteStream().use { - read = it.read(d) - while (read != -1) { - messageDigest.update(d, 0, read) - decodedBytes = decryptCipher.update(d, 0, read) - outputStream.write(decodedBytes) - read = it.read(d) - } - } - } - return outputStream + return response.body?.byteStream()?.let { mediaDecrypter.decrypt(it, keys) } ?: Buffer() } -} \ No newline at end of file +} + diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MediaDecrypter.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MediaDecrypter.kt new file mode 100644 index 0000000..9e18c76 --- /dev/null +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MediaDecrypter.kt @@ -0,0 +1,47 @@ +package app.dapk.st.messenger + +import android.util.Base64 +import app.dapk.st.matrix.sync.RoomEvent +import okio.Buffer +import java.io.InputStream +import java.security.MessageDigest +import javax.crypto.Cipher +import javax.crypto.spec.IvParameterSpec +import javax.crypto.spec.SecretKeySpec + +private const val CRYPTO_BUFFER_SIZE = 32 * 1024 +private const val CIPHER_ALGORITHM = "AES/CTR/NoPadding" +private const val SECRET_KEY_SPEC_ALGORITHM = "AES" +private const val MESSAGE_DIGEST_ALGORITHM = "SHA-256" + +class MediaDecrypter { + + fun decrypt(input: InputStream, keys: RoomEvent.Image.ImageMeta.Keys): Buffer { + val key = Base64.decode(keys.k.replace('-', '+').replace('_', '/'), Base64.DEFAULT) + val initVectorBytes = Base64.decode(keys.iv, Base64.DEFAULT) + + val decryptCipher = Cipher.getInstance(CIPHER_ALGORITHM) + val secretKeySpec = SecretKeySpec(key, SECRET_KEY_SPEC_ALGORITHM) + val ivParameterSpec = IvParameterSpec(initVectorBytes) + decryptCipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec) + + val messageDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM) + + var read: Int + val d = ByteArray(CRYPTO_BUFFER_SIZE) + var decodedBytes: ByteArray + + val outputStream = Buffer() + input.use { + read = it.read(d) + while (read != -1) { + messageDigest.update(d, 0, read) + decodedBytes = decryptCipher.update(d, 0, read) + outputStream.write(decodedBytes) + read = it.read(d) + } + } + return outputStream + } + +} \ No newline at end of file diff --git a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendMessageUseCase.kt b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendMessageUseCase.kt index b094e4f..646b716 100644 --- a/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendMessageUseCase.kt +++ b/matrix/services/message/src/main/kotlin/app/dapk/st/matrix/message/internal/SendMessageUseCase.kt @@ -38,22 +38,31 @@ internal class SendMessageUseCase( } is MessageService.Message.ImageMessage -> { - val imageContent = imageContentReader.read(message.content.uri) - val uri = httpClient.execute(uploadRequest(imageContent.content, imageContent.fileName, imageContent.mimeType)).contentUri - val request = sendRequest( - roomId = message.roomId, - eventType = EventType.ROOM_MESSAGE, - txId = message.localId, - content = MessageService.Message.Content.ImageContent( - url = uri, - filename = imageContent.fileName, - MessageService.Message.Content.ImageContent.Info( - height = imageContent.height, - width = imageContent.width, - size = imageContent.size + val request = when (message.sendEncrypted) { + true -> { + throw IllegalStateException() + } + + false -> { + val imageContent = imageContentReader.read(message.content.uri) + val uri = httpClient.execute(uploadRequest(imageContent.content, imageContent.fileName, imageContent.mimeType)).contentUri + sendRequest( + roomId = message.roomId, + eventType = EventType.ROOM_MESSAGE, + txId = message.localId, + content = MessageService.Message.Content.ImageContent( + url = uri, + filename = imageContent.fileName, + MessageService.Message.Content.ImageContent.Info( + height = imageContent.height, + width = imageContent.width, + size = imageContent.size + ) + ), ) - ), - ) + } + } + httpClient.execute(request).eventId } }