diff --git a/libsession-util/src/main/cpp/config_base.cpp b/libsession-util/src/main/cpp/config_base.cpp index 7b45d446ea..1c90b1b81c 100644 --- a/libsession-util/src/main/cpp/config_base.cpp +++ b/libsession-util/src/main/cpp/config_base.cpp @@ -82,7 +82,7 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_confirmPushed(JNIEnv *en #pragma clang diagnostic push #pragma ide diagnostic ignored "bugprone-reserved-identifier" -JNIEXPORT jint JNICALL +JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_merge___3Lkotlin_Pair_2(JNIEnv *env, jobject thiz, jobjectArray to_merge) { std::lock_guard lock{util::util_mutex_}; @@ -94,21 +94,31 @@ Java_network_loki_messenger_libsession_1util_ConfigBase_merge___3Lkotlin_Pair_2( auto pair = extractHashAndData(env, jElement); configs.push_back(pair); } - return conf->merge(configs); + auto returned = conf->merge(configs); + auto string_stack = util::build_string_stack(env, returned); + return string_stack; } -JNIEXPORT jint JNICALL +JNIEXPORT jobject JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_merge__Lkotlin_Pair_2(JNIEnv *env, jobject thiz, jobject to_merge) { std::lock_guard lock{util::util_mutex_}; auto conf = ptrToConfigBase(env, thiz); std::vector> configs = {extractHashAndData(env, to_merge)}; - return conf->merge(configs); + auto returned = conf->merge(configs); + auto string_stack = util::build_string_stack(env, returned); + return string_stack; } #pragma clang diagnostic pop } extern "C" +JNIEXPORT jint JNICALL +Java_network_loki_messenger_libsession_1util_ConfigBase_configNamespace(JNIEnv *env, jobject thiz) { + auto conf = ptrToConfigBase(env, thiz); + return (std::int16_t) conf->storage_namespace(); +} +extern "C" JNIEXPORT jclass JNICALL Java_network_loki_messenger_libsession_1util_ConfigBase_00024Companion_kindFor(JNIEnv *env, jobject thiz, diff --git a/libsession-util/src/main/cpp/util.cpp b/libsession-util/src/main/cpp/util.cpp index 55f42a4180..72453cd6fd 100644 --- a/libsession-util/src/main/cpp/util.cpp +++ b/libsession-util/src/main/cpp/util.cpp @@ -186,6 +186,17 @@ namespace util { return group_member; } + jobject build_string_stack(JNIEnv* env, std::vector to_add) { + jclass stack_class = env->FindClass("java/util/Stack"); + jmethodID constructor = env->GetMethodID(stack_class,"", "()V"); + jmethodID add = env->GetMethodID(stack_class, "push", "(Ljava/lang/Object;)Ljava/lang/Object;"); + jobject our_stack = env->NewObject(stack_class, constructor); + for (std::basic_string_view string: to_add) { + env->CallObjectMethod(our_stack, add, env->NewStringUTF(string.data())); + } + return our_stack; + } + jobject jlongFromOptional(JNIEnv* env, std::optional optional) { if (!optional) { return nullptr; diff --git a/libsession-util/src/main/cpp/util.h b/libsession-util/src/main/cpp/util.h index e6d16d9bdc..42c2220f43 100644 --- a/libsession-util/src/main/cpp/util.h +++ b/libsession-util/src/main/cpp/util.h @@ -2,7 +2,6 @@ #define SESSION_ANDROID_UTIL_H #include -#include #include #include #include "session/types.hpp" @@ -12,13 +11,6 @@ #include "session/config/profile_pic.hpp" #include "session/config/user_groups.hpp" #include "session/config/expiring.hpp" -//#include - -//#define LOG_TAG "libsession-jni" -//#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) -//#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) -//#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__) -//#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) namespace util { extern std::mutex util_mutex_; @@ -37,6 +29,7 @@ namespace util { jstring jstringFromOptional(JNIEnv* env, std::optional optional); jobject serialize_session_id(JNIEnv* env, std::string_view session_id); std::string deserialize_session_id(JNIEnv* env, jobject session_id); + jobject build_string_stack(JNIEnv* env, std::vector to_add); } #endif \ No newline at end of file diff --git a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt index 48c4fb3ab6..ba53f9e681 100644 --- a/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt +++ b/libsession-util/src/main/java/network/loki/messenger/libsession_util/Config.kt @@ -57,11 +57,11 @@ sealed class ConfigBase(pointer: Long): Config(pointer) { external fun dump(): ByteArray external fun encryptionDomain(): String external fun confirmPushed(seqNo: Long, newHash: String) - external fun merge(toMerge: Array>): Int + external fun merge(toMerge: Array>): Stack external fun currentHashes(): List // Singular merge - external fun merge(toMerge: Pair): Int + external fun merge(toMerge: Pair): Stack } diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt index 6aae0c6c9b..d78ac91ee5 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/AttachmentDownloadJob.kt @@ -100,7 +100,8 @@ class AttachmentDownloadJob(val attachmentID: Long, val databaseMessageID: Long) handleFailure(Error.NoSender, null) return } - if (!threadRecipient.isGroupRecipient && contact?.isTrusted != true && storage.getUserPublicKey() != sender) { + + if (!storage.shouldAutoDownloadAttachments(threadRecipient)) { // if we aren't receiving a group message, a message from ourselves (self-send) and the contact sending is not trusted: // do not continue, but do not fail handleFailure(Error.NoSender, null) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt index 65300f1cf3..172e2837a7 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/BatchMessageReceiveJob.kt @@ -8,6 +8,7 @@ import kotlinx.coroutines.runBlocking import nl.komponents.kovenant.Promise import nl.komponents.kovenant.task import org.session.libsession.messaging.MessagingModuleConfiguration +import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.Message import org.session.libsession.messaging.messages.control.CallMessage import org.session.libsession.messaging.messages.control.ClosedGroupControlMessage @@ -28,18 +29,19 @@ import org.session.libsession.messaging.sending_receiving.handleOpenGroupReactio import org.session.libsession.messaging.sending_receiving.handleUnsendRequest import org.session.libsession.messaging.sending_receiving.handleVisibleMessage import org.session.libsession.messaging.utilities.Data -import org.session.libsignal.utilities.SessionId import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.utilities.SSKEnvironment import org.session.libsignal.protos.UtilProtos import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.Log +import org.session.libsignal.utilities.SessionId data class MessageReceiveParameters( val data: ByteArray, val serverHash: String? = null, val openGroupMessageServerID: Long? = null, - val reactions: Map? = null + val reactions: Map? = null, + val closedGroup: Destination.ClosedGroup? = null ) class BatchMessageReceiveJob( @@ -69,6 +71,7 @@ class BatchMessageReceiveJob( private val SERVER_HASH_KEY = "serverHash" private val OPEN_GROUP_MESSAGE_SERVER_ID_KEY = "openGroupMessageServerID" private val OPEN_GROUP_ID_KEY = "open_group_id" + private val CLOSED_GROUP_DESTINATION_KEY = "closed_group_destination" } private fun shouldCreateThread(parsedMessage: ParsedMessage): Boolean { @@ -108,7 +111,13 @@ class BatchMessageReceiveJob( messages.forEach { messageParameters -> val (data, serverHash, openGroupMessageServerID) = messageParameters try { - val (message, proto) = MessageReceiver.parse(data, openGroupMessageServerID, openGroupPublicKey = serverPublicKey, currentClosedGroups = currentClosedGroups) + val (message, proto) = MessageReceiver.parse( + data, + openGroupMessageServerID, + openGroupPublicKey = serverPublicKey, + currentClosedGroups = currentClosedGroups, + closedGroupSessionId = messageParameters.closedGroup?.publicKey + ) message.serverHash = serverHash val parsedParams = ParsedMessage(messageParameters, message, proto) val threadID = Message.getThreadId(message, openGroupID, storage, shouldCreateThread(parsedParams)) ?: NO_THREAD_MAPPING @@ -262,12 +271,14 @@ class BatchMessageReceiveJob( .build() val serverHashes = messages.map { it.serverHash.orEmpty() } val openGroupServerIds = messages.map { it.openGroupMessageServerID ?: -1L } + val closedGroups = messages.map { it.closedGroup?.publicKey.orEmpty() } return Data.Builder() .putInt(NUM_MESSAGES_KEY, arraySize) .putByteArray(DATA_KEY, dataArrays.toByteArray()) .putString(OPEN_GROUP_ID_KEY, openGroupID) .putLongArray(OPEN_GROUP_MESSAGE_SERVER_ID_KEY, openGroupServerIds.toLongArray()) .putStringArray(SERVER_HASH_KEY, serverHashes.toTypedArray()) + .putStringArray(CLOSED_GROUP_DESTINATION_KEY, closedGroups.toTypedArray()) .build() } @@ -283,11 +294,22 @@ class BatchMessageReceiveJob( if (data.hasStringArray(SERVER_HASH_KEY)) data.getStringArray(SERVER_HASH_KEY) else arrayOf() val openGroupMessageServerIDs = data.getLongArray(OPEN_GROUP_MESSAGE_SERVER_ID_KEY) val openGroupID = data.getStringOrDefault(OPEN_GROUP_ID_KEY, null) + val closedGroups = + if (data.hasStringArray(CLOSED_GROUP_DESTINATION_KEY)) data.getStringArray(CLOSED_GROUP_DESTINATION_KEY) + else arrayOf() val parameters = (0 until numMessages).map { index -> val serverHash = serverHashes[index].let { if (it.isEmpty()) null else it } val serverId = openGroupMessageServerIDs[index].let { if (it == -1L) null else it } - MessageReceiveParameters(contents[index], serverHash, serverId) + val closedGroup = closedGroups.getOrNull(index)?.let { + if (it.isEmpty()) null else Destination.ClosedGroup(it) + } + MessageReceiveParameters( + data = contents[index], + serverHash = serverHash, + openGroupMessageServerID = serverId, + closedGroup = closedGroup + ) } return BatchMessageReceiveJob(parameters, openGroupID) diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt index 3077f3dcd3..db2f51714c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt @@ -17,6 +17,7 @@ import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.snode.SnodeAPI import org.session.libsignal.crypto.PushTransportDetails import org.session.libsignal.protos.SignalServiceProtos +import org.session.libsignal.protos.SignalServiceProtos.Envelope import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.SessionId @@ -53,22 +54,24 @@ object MessageReceiver { isOutgoing: Boolean? = null, otherBlindedPublicKey: String? = null, openGroupPublicKey: String? = null, - currentClosedGroups: Set? + currentClosedGroups: Set?, + closedGroupSessionId: String? = null, ): Pair { val storage = MessagingModuleConfiguration.shared.storage val userPublicKey = storage.getUserPublicKey() val isOpenGroupMessage = (openGroupServerID != null) - // Parse the envelope - val envelope = SignalServiceProtos.Envelope.parseFrom(data) - // Decrypt the contents - val ciphertext = envelope.content ?: run { - throw Error.NoData - } var plaintext: ByteArray? = null var sender: String? = null var groupPublicKey: String? = null + // Parse the envelope + val envelope = Envelope.parseFrom(data) ?: throw Error.InvalidMessage + // Decrypt the contents + val envelopeContent = envelope.content ?: run { + throw Error.NoData + } + if (isOpenGroupMessage) { - plaintext = envelope.content.toByteArray() + plaintext = envelopeContent.toByteArray() sender = envelope.source } else { when (envelope.type) { @@ -77,7 +80,7 @@ object MessageReceiver { openGroupPublicKey ?: throw Error.InvalidGroupPublicKey otherBlindedPublicKey ?: throw Error.DecryptionFailed val decryptionResult = MessageDecrypter.decryptBlinded( - ciphertext.toByteArray(), + envelopeContent.toByteArray(), isOutgoing ?: false, otherBlindedPublicKey, openGroupPublicKey @@ -86,26 +89,18 @@ object MessageReceiver { sender = decryptionResult.second } else { val userX25519KeyPair = MessagingModuleConfiguration.shared.storage.getUserX25519KeyPair() - val decryptionResult = MessageDecrypter.decrypt(ciphertext.toByteArray(), userX25519KeyPair) + val decryptionResult = MessageDecrypter.decrypt(envelopeContent.toByteArray(), userX25519KeyPair) plaintext = decryptionResult.first sender = decryptionResult.second } } SignalServiceProtos.Envelope.Type.CLOSED_GROUP_MESSAGE -> { - val hexEncodedGroupPublicKey = envelope.source + val hexEncodedGroupPublicKey = closedGroupSessionId ?: envelope.source val sessionId = SessionId.from(hexEncodedGroupPublicKey) if (sessionId.prefix == IdPrefix.GROUP) { - val configFactory = MessagingModuleConfiguration.shared.configFactory - configFactory.getGroupKeysConfig(sessionId)?.use { config -> - config.decrypt(ciphertext.toByteArray())?.let { (decrypted, senderSessionId) -> - plaintext = decrypted - sender = senderSessionId.hexString() - groupPublicKey = envelope.source - } - } - if (plaintext == null) { - throw Error.DecryptionFailed - } + plaintext = envelopeContent.toByteArray() + sender = envelope.source + groupPublicKey = hexEncodedGroupPublicKey } else { if (!MessagingModuleConfiguration.shared.storage.isLegacyClosedGroup(hexEncodedGroupPublicKey)) { throw Error.InvalidGroupPublicKey @@ -119,7 +114,7 @@ object MessageReceiver { var encryptionKeyPair = encryptionKeyPairs.removeLast() fun decrypt() { try { - val decryptionResult = MessageDecrypter.decrypt(ciphertext.toByteArray(), encryptionKeyPair) + val decryptionResult = MessageDecrypter.decrypt(envelopeContent.toByteArray(), encryptionKeyPair) plaintext = decryptionResult.first sender = decryptionResult.second } catch (e: Exception) { @@ -132,7 +127,7 @@ object MessageReceiver { } } } - groupPublicKey = envelope.source + groupPublicKey = hexEncodedGroupPublicKey decrypt() } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt index eb4f6e19fc..f102659369 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -125,25 +125,8 @@ object MessageSender { val proto = message.toProto() ?: throw Error.ProtoConversionFailed // Serialize the protobuf val plaintext = PushTransportDetails.getPaddedMessageBody(proto.toByteArray()) - // Encrypt the serialized protobuf - val ciphertext = when (destination) { - is Destination.Contact -> MessageEncrypter.encrypt(plaintext, destination.publicKey) - is Destination.LegacyClosedGroup -> { - val encryptionKeyPair = - MessagingModuleConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair( - destination.groupPublicKey - )!! - MessageEncrypter.encrypt(plaintext, encryptionKeyPair.hexEncodedPublicKey) - } - is Destination.ClosedGroup -> { - val groupKeys = configFactory.getGroupKeysConfig(SessionId.from(destination.publicKey)) ?: throw Error.NoKeyPair - groupKeys.use { keys -> - keys.encrypt(proto.toByteArray()) - } - } - else -> throw IllegalStateException("Destination should not be open group.") - } - // Wrap the result + + // Envelope information val kind: SignalServiceProtos.Envelope.Type val senderPublicKey: String when (destination) { @@ -161,7 +144,34 @@ object MessageSender { } else -> throw IllegalStateException("Destination should not be open group.") } - val wrappedMessage = MessageWrapper.wrap(kind, message.sentTimestamp!!, senderPublicKey, ciphertext) + + // Encrypt the serialized protobuf + val ciphertext = when (destination) { + is Destination.Contact -> MessageEncrypter.encrypt(plaintext, destination.publicKey) + is Destination.LegacyClosedGroup -> { + val encryptionKeyPair = + MessagingModuleConfiguration.shared.storage.getLatestClosedGroupEncryptionKeyPair( + destination.groupPublicKey + )!! + MessageEncrypter.encrypt(plaintext, encryptionKeyPair.hexEncodedPublicKey) + } + is Destination.ClosedGroup -> { + val groupKeys = configFactory.getGroupKeysConfig(SessionId.from(destination.publicKey)) ?: throw Error.NoKeyPair + val envelope = MessageWrapper.createEnvelope(kind, message.sentTimestamp!!, senderPublicKey, proto.toByteArray()) + groupKeys.use { keys -> + keys.encrypt(envelope.toByteArray()) + } + } + else -> throw IllegalStateException("Destination should not be open group.") + } + // Wrap the result using envelope information + val wrappedMessage = when (destination) { + is Destination.ClosedGroup -> { + // encrypted bytes from the above closed group encryption and envelope steps + ciphertext + } + else -> MessageWrapper.wrap(kind, message.sentTimestamp!!, senderPublicKey, ciphertext) + } val base64EncodedData = Base64.encodeBytes(wrappedMessage) // Send the result return SnodeMessage( diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPoller.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPoller.kt index 58bd18cd99..68c8d31dfc 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPoller.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/pollers/ClosedGroupPoller.kt @@ -165,7 +165,7 @@ class ClosedGroupPoller(private val executor: CoroutineScope, keysIndex -> handleKeyPoll(response, keys, info, members) infoIndex -> handleInfo(response, info) membersIndex -> handleMembers(response, members) - messageIndex -> handleMessages(response, snode) + messageIndex -> handleMessages(response, snode, keys) } } @@ -234,11 +234,13 @@ class ClosedGroupPoller(private val executor: CoroutineScope, } } - private fun handleMessages(response: RawResponse, snode: Snode) { + private fun handleMessages(response: RawResponse, snode: Snode, keysConfig: GroupKeysConfig) { val body = response["body"] as RawResponse - val messages = SnodeAPI.parseRawMessagesResponse(body, snode, closedGroupSessionId.hexString()) + val messages = SnodeAPI.parseRawMessagesResponse(body, snode, closedGroupSessionId.hexString()) { + return@parseRawMessagesResponse keysConfig.decrypt(it) + } val parameters = messages.map { (envelope, serverHash) -> - MessageReceiveParameters(envelope.toByteArray(), serverHash = serverHash) + MessageReceiveParameters(envelope.toByteArray(), serverHash = serverHash, closedGroup = Destination.ClosedGroup(closedGroupSessionId.hexString())) } parameters.chunked(BatchMessageReceiveJob.BATCH_DEFAULT_NUMBER).forEach { chunk -> val job = BatchMessageReceiveJob(chunk) diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/MessageWrapper.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/MessageWrapper.kt index 569b9c62c1..71ee3096bb 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/MessageWrapper.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/MessageWrapper.kt @@ -1,10 +1,10 @@ package org.session.libsession.messaging.utilities import com.google.protobuf.ByteString -import org.session.libsignal.utilities.Log import org.session.libsignal.protos.SignalServiceProtos.Envelope import org.session.libsignal.protos.WebSocketProtos.WebSocketMessage import org.session.libsignal.protos.WebSocketProtos.WebSocketRequestMessage +import org.session.libsignal.utilities.Log import java.security.SecureRandom object MessageWrapper { @@ -32,7 +32,7 @@ object MessageWrapper { } } - private fun createEnvelope(type: Envelope.Type, timestamp: Long, senderPublicKey: String, content: ByteArray): Envelope { + fun createEnvelope(type: Envelope.Type, timestamp: Long, senderPublicKey: String, content: ByteArray): Envelope { try { val builder = Envelope.newBuilder() builder.type = type diff --git a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt index 657f07c99d..42eacf7b24 100644 --- a/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt +++ b/libsession/src/main/java/org/session/libsession/snode/SnodeAPI.kt @@ -29,6 +29,7 @@ import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.JsonUtil import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Namespace +import org.session.libsignal.utilities.SessionId import org.session.libsignal.utilities.Snode import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.prettifiedDescription @@ -851,14 +852,21 @@ object SnodeAPI { } } - fun parseRawMessagesResponse(rawResponse: RawResponse, snode: Snode, publicKey: String, namespace: Int = 0, updateLatestHash: Boolean = true, updateStoredHashes: Boolean = true): List> { + fun parseRawMessagesResponse(rawResponse: RawResponse, + snode: Snode, + publicKey: String, + namespace: Int = 0, + updateLatestHash: Boolean = true, + updateStoredHashes: Boolean = true, + decrypt: ((ByteArray) -> Pair?)? = null + ): List> { val messages = rawResponse["messages"] as? List<*> return if (messages != null) { if (updateLatestHash) { updateLastMessageHashValueIfPossible(snode, publicKey, messages, namespace) } val newRawMessages = removeDuplicates(publicKey, messages, namespace, updateStoredHashes) - return parseEnvelopes(newRawMessages) + return parseEnvelopes(newRawMessages, decrypt) } else { listOf() } @@ -895,14 +903,20 @@ object SnodeAPI { return result } - private fun parseEnvelopes(rawMessages: List<*>): List> { + private fun parseEnvelopes(rawMessages: List<*>, decrypt: ((ByteArray)->Pair?)?): List> { return rawMessages.mapNotNull { rawMessage -> val rawMessageAsJSON = rawMessage as? Map<*, *> val base64EncodedData = rawMessageAsJSON?.get("data") as? String val data = base64EncodedData?.let { Base64.decode(it) } if (data != null) { try { - Pair(MessageWrapper.unwrap(data), rawMessageAsJSON.get("hash") as? String) + if (decrypt != null) { + val (decrypted, sender) = decrypt(data)!! + val envelope = SignalServiceProtos.Envelope.parseFrom(decrypted).toBuilder() + envelope.source = sender.hexString() + Pair(envelope.build(),rawMessageAsJSON.get("hash") as? String) + } + else Pair(MessageWrapper.unwrap(data), rawMessageAsJSON.get("hash") as? String) } catch (e: Exception) { Log.d("Loki", "Failed to unwrap data for message: ${rawMessage.prettifiedDescription()}.") null