feat: hacky workaround for new protobuf encryption branches !

This commit is contained in:
0x330a
2023-10-16 17:41:00 +11:00
parent fcb1c56edb
commit 00ad9cf39f
11 changed files with 131 additions and 73 deletions

View File

@@ -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)

View File

@@ -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<String, OpenGroupApi.Reaction>? = null
val reactions: Map<String, OpenGroupApi.Reaction>? = 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)

View File

@@ -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<String>?
currentClosedGroups: Set<String>?,
closedGroupSessionId: String? = null,
): Pair<Message, SignalServiceProtos.Content> {
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()
}
}

View File

@@ -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(

View File

@@ -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)

View File

@@ -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

View File

@@ -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<Pair<SignalServiceProtos.Envelope, String?>> {
fun parseRawMessagesResponse(rawResponse: RawResponse,
snode: Snode,
publicKey: String,
namespace: Int = 0,
updateLatestHash: Boolean = true,
updateStoredHashes: Boolean = true,
decrypt: ((ByteArray) -> Pair<ByteArray, SessionId>?)? = null
): List<Pair<SignalServiceProtos.Envelope, String?>> {
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<Pair<SignalServiceProtos.Envelope, String?>> {
private fun parseEnvelopes(rawMessages: List<*>, decrypt: ((ByteArray)->Pair<ByteArray, SessionId>?)?): List<Pair<SignalServiceProtos.Envelope, String?>> {
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