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
No known key found for this signature in database
GPG Key ID: 267811D6E6A2698C
11 changed files with 131 additions and 73 deletions

View File

@ -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<std::pair<std::string, session::ustring>> 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,

View File

@ -186,6 +186,17 @@ namespace util {
return group_member;
}
jobject build_string_stack(JNIEnv* env, std::vector<std::string> to_add) {
jclass stack_class = env->FindClass("java/util/Stack");
jmethodID constructor = env->GetMethodID(stack_class,"<init>", "()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<char> string: to_add) {
env->CallObjectMethod(our_stack, add, env->NewStringUTF(string.data()));
}
return our_stack;
}
jobject jlongFromOptional(JNIEnv* env, std::optional<long long> optional) {
if (!optional) {
return nullptr;

View File

@ -2,7 +2,6 @@
#define SESSION_ANDROID_UTIL_H
#include <jni.h>
#include <mutex>
#include <array>
#include <optional>
#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 <android/log.h>
//#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<std::string_view> 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<std::string> to_add);
}
#endif

View File

@ -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<Pair<String,ByteArray>>): Int
external fun merge(toMerge: Array<Pair<String,ByteArray>>): Stack<String>
external fun currentHashes(): List<String>
// Singular merge
external fun merge(toMerge: Pair<String,ByteArray>): Int
external fun merge(toMerge: Pair<String,ByteArray>): Stack<String>
}

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