commit b17a8d245418d9352c0bfcdfb77390ecb890274b
parent 2905fe6e2dd21fba1d494e695e0921a15660e50c
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date:   Sun, 14 Jan 2024 11:33:35 +0100

feat(core/e2ee): sent messages indicator
- add warn message for decryption failure

Diffstat:
Mcore/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/EndToEndEncryption.kt | 42+++++++++++++++++++++++++++++++++---------
1 file changed, 33 insertions(+), 9 deletions(-)

diff --git a/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/EndToEndEncryption.kt b/core/src/main/kotlin/me/rhunk/snapenhance/core/features/impl/experiments/EndToEndEncryption.kt @@ -16,7 +16,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import me.rhunk.snapenhance.common.data.ContentType -import me.rhunk.snapenhance.common.data.MessageState import me.rhunk.snapenhance.common.data.MessagingRuleType import me.rhunk.snapenhance.common.data.RuleState import me.rhunk.snapenhance.common.util.protobuf.ProtoEditor @@ -33,9 +32,14 @@ import me.rhunk.snapenhance.core.ui.ViewAppearanceHelper import me.rhunk.snapenhance.core.ui.addForegroundDrawable import me.rhunk.snapenhance.core.ui.removeForegroundDrawable import me.rhunk.snapenhance.core.util.EvictingMap +import me.rhunk.snapenhance.core.util.hook.HookStage +import me.rhunk.snapenhance.core.util.hook.hook import me.rhunk.snapenhance.core.util.ktx.getObjectField +import me.rhunk.snapenhance.core.util.ktx.getObjectFieldOrNull import me.rhunk.snapenhance.core.wrapper.impl.MessageContent +import me.rhunk.snapenhance.core.wrapper.impl.MessageDestinations import me.rhunk.snapenhance.core.wrapper.impl.SnapUUID +import me.rhunk.snapenhance.mapper.impl.CallbackMapper import me.rhunk.snapenhance.nativelib.NativeLib import java.security.MessageDigest import kotlin.random.Random @@ -282,6 +286,16 @@ class EndToEndEncryption : MessagingRuleFeature( encryptedMessages.add(clientMessageId) } + fun setWarningMessage() { + encryptedMessages.add(clientMessageId) + outputContentType = ContentType.CHAT + outputBuffer = ProtoWriter().apply { + from(2) { + addString(1, "Failed to decrypt message, id=$clientMessageId. Check logs for more details.") + } + }.toByteArray() + } + fun replaceMessageText(text: String) { outputBuffer = ProtoWriter().apply { from(2) { @@ -309,6 +323,7 @@ class EndToEndEncryption : MessagingRuleFeature( val participantId = conversationParticipants.firstOrNull { participantIdHash.contentEquals(hashParticipantId(it, iv)) } ?: return@eachBuffer setDecryptedMessage(e2eeInterface.decryptMessage(participantId, ciphertext, iv) ?: run { context.log.warn("Failed to decrypt message for participant $participantId") + setWarningMessage() return@eachBuffer }) return@eachBuffer @@ -317,18 +332,13 @@ class EndToEndEncryption : MessagingRuleFeature( if (!participantIdHash.contentEquals(hashParticipantId(context.database.myUserId, iv))) return@eachBuffer setDecryptedMessage(e2eeInterface.decryptMessage(senderId, ciphertext, iv)?: run { - context.log.warn("Failed to decrypt message") + setWarningMessage() return@eachBuffer }) } }.onFailure { context.log.error("Failed to decrypt message id: $clientMessageId", it) - outputContentType = ContentType.CHAT - outputBuffer = ProtoWriter().apply { - from(2) { - addString(1, "Failed to decrypt message, id=$clientMessageId. Check logcat for more details.") - } - }.toByteArray() + setWarningMessage() } return@followPath @@ -492,9 +502,23 @@ class EndToEndEncryption : MessagingRuleFeature( override fun init() { if (!isEnabled) return + context.mappings.useMapper(CallbackMapper::class) { + callbacks.getClass("ConversationManagerDelegate")?.hook("onSendComplete", HookStage.BEFORE) { param -> + val sendMessageResult = param.arg<Any>(0) + val messageDestinations = MessageDestinations(sendMessageResult.getObjectField("mCompletedDestinations") ?: return@hook) + if (messageDestinations.mPhoneNumbers?.isNotEmpty() == true || messageDestinations.stories?.isNotEmpty() == true) return@hook + + val completedConversationDestinations = sendMessageResult.getObjectField("mCompletedConversationDestinations") as? ArrayList<*> ?: return@hook + val messageIds = completedConversationDestinations.filter { getState(SnapUUID(it.getObjectField("mConversationId")).toString()) }.mapNotNull { + it.getObjectFieldOrNull("mMessageId") as? Long + } + + encryptedMessages.addAll(messageIds) + } + } + context.event.subscribe(BuildMessageEvent::class, priority = 0) { event -> val message = event.message - if (message.messageState != MessageState.COMMITTED) return@subscribe val conversationId = message.messageDescriptor!!.conversationId.toString() messageHook( conversationId = conversationId,