From a24225cf8b8c9bdf76d11af965df6c2fa81b2051 Mon Sep 17 00:00:00 2001 From: ceokot Date: Tue, 3 May 2022 15:23:58 +1000 Subject: [PATCH] Open group message requests --- .../conversation/v2/ConversationActivityV2.kt | 19 ++++++++++- .../v2/messages/VisibleMessageView.kt | 15 ++++++-- .../securesms/util/ContactUtilities.kt | 21 ++++++++++++ .../messaging/messages/Destination.kt | 21 +++++------- .../messaging/open_groups/OpenGroupApi.kt | 5 +-- .../sending_receiving/MessageDecrypter.kt | 4 ++- .../sending_receiving/MessageSender.kt | 14 ++++++-- .../messaging/utilities/SodiumUtilities.kt | 34 +++++++++++-------- .../session/libsession/utilities/Address.kt | 2 ++ .../session/libsession/utilities/GroupUtil.kt | 14 +++++++- 10 files changed, 114 insertions(+), 35 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index a8d0a2e30a..e4bc16725a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -62,14 +62,17 @@ import org.session.libsession.messaging.sending_receiving.MessageSender import org.session.libsession.messaging.sending_receiving.attachments.Attachment import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel +import org.session.libsession.messaging.utilities.SessionId import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address.Companion.fromSerialized +import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.MediaTypes import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.concurrent.SimpleTask import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.RecipientModifiedListener import org.session.libsignal.crypto.MnemonicCodec +import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.ListenableFuture import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.utilities.hexEncodedPrivateKey @@ -129,6 +132,7 @@ import org.thoughtcrime.securesms.mms.VideoSlide import org.thoughtcrime.securesms.permissions.Permissions import org.thoughtcrime.securesms.util.ActivityDispatcher import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities +import org.thoughtcrime.securesms.util.ContactUtilities import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.MediaUtil import org.thoughtcrime.securesms.util.SaveAttachmentTask @@ -176,7 +180,19 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe private val viewModel: ConversationViewModel by viewModels { var threadId = intent.getLongExtra(THREAD_ID, -1L) if (threadId == -1L) { - intent.getParcelableExtra
(ADDRESS)?.let { address -> + intent.getParcelableExtra
(ADDRESS)?.let { it -> + val sessionId = SessionId(it.serialize()) + val openGroup = lokiThreadDb.getOpenGroupChat(intent.getLongExtra(FROM_OPEN_GROUP_THREAD_ID, -1)) + val address = if (sessionId.prefix == IdPrefix.BLINDED && openGroup != null) { + ContactUtilities.getBlindedIdMapping(this, sessionId.publicKey, openGroup.publicKey)?.let { + fromSerialized(it.sessionId) + } ?: run { + val openGroupInboxId = "${openGroup.server}.${openGroup.publicKey}.${sessionId.hexString}".toByteArray() + fromSerialized(GroupUtil.getEncodedOpenGroupInboxID(openGroupInboxId)) + } + } else { + it + } val recipient = Recipient.from(this, address, false) threadId = threadDb.getOrCreateThreadIdFor(recipient) } ?: finish() @@ -261,6 +277,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe // Extras const val THREAD_ID = "thread_id" const val ADDRESS = "address" + const val FROM_OPEN_GROUP_THREAD_ID = "from_open_group_id" const val SCROLL_MESSAGE_ID = "scroll_message_id" const val SCROLL_MESSAGE_AUTHOR = "scroll_message_author" // Request codes diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt index c02430aebd..d7d869ed64 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/VisibleMessageView.kt @@ -1,6 +1,7 @@ package org.thoughtcrime.securesms.conversation.v2.messages import android.content.Context +import android.content.Intent import android.content.res.Resources import android.graphics.Canvas import android.graphics.Rect @@ -25,9 +26,12 @@ import network.loki.messenger.R import network.loki.messenger.databinding.ViewVisibleMessageBinding import org.session.libsession.messaging.contacts.Contact.ContactContext import org.session.libsession.messaging.open_groups.OpenGroupApi +import org.session.libsession.utilities.Address import org.session.libsession.utilities.ViewUtil +import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.ThreadUtils import org.thoughtcrime.securesms.ApplicationContext +import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.database.LokiThreadDatabase import org.thoughtcrime.securesms.database.MmsDatabase import org.thoughtcrime.securesms.database.MmsSmsDatabase @@ -123,7 +127,14 @@ class VisibleMessageView : LinearLayout { binding.profilePictureView.glide = glide binding.profilePictureView.update(message.individualRecipient) binding.profilePictureView.setOnClickListener { - showUserDetails(senderSessionID, threadID) + if (thread.isOpenGroupRecipient && IdPrefix.fromValue(senderSessionID) == IdPrefix.BLINDED) { + val intent = Intent(context, ConversationActivityV2::class.java) + intent.putExtra(ConversationActivityV2.FROM_OPEN_GROUP_THREAD_ID, threadID) + intent.putExtra(ConversationActivityV2.ADDRESS, Address.fromSerialized(senderSessionID)) + context.startActivity(intent) + } else { + maybeShowUserDetails(senderSessionID, threadID) + } } if (thread.isOpenGroupRecipient) { val openGroup = lokiThreadDb.getOpenGroupChat(threadID) ?: return @@ -398,7 +409,7 @@ class VisibleMessageView : LinearLayout { pressCallback = null } - private fun showUserDetails(publicKey: String, threadID: Long) { + private fun maybeShowUserDetails(publicKey: String, threadID: Long) { val userDetailsBottomSheet = UserDetailsBottomSheet() val bundle = bundleOf( UserDetailsBottomSheet.ARGUMENT_PUBLIC_KEY to publicKey, diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ContactUtilities.kt b/app/src/main/java/org/thoughtcrime/securesms/util/ContactUtilities.kt index 8b0ab5d345..a6b725b74c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ContactUtilities.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ContactUtilities.kt @@ -1,6 +1,8 @@ package org.thoughtcrime.securesms.util import android.content.Context +import org.session.libsession.messaging.utilities.BlindedIdMapping +import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.utilities.recipients.Recipient import org.thoughtcrime.securesms.dependencies.DatabaseComponent @@ -20,4 +22,23 @@ object ContactUtilities { } return result } + + fun getBlindedIdMapping( + context: Context, + blindedSessionId: String, + serverPublicKey: String + ): BlindedIdMapping? { + val threadDatabase = DatabaseComponent.get(context).threadDatabase() + val cursor = threadDatabase.approvedConversationList + threadDatabase.readerFor(cursor).use { reader -> + while (reader.next != null) { + val recipient = reader.current.recipient + val sessionId = recipient.address.serialize() + if (!recipient.isGroupRecipient && SodiumUtilities.sessionId(sessionId, blindedSessionId, serverPublicKey)) { + return BlindedIdMapping(blindedSessionId, sessionId, serverPublicKey) + } + } + } + return null + } } \ No newline at end of file diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt index a1548c40b6..3a97a2ee94 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/Destination.kt @@ -1,10 +1,8 @@ package org.session.libsession.messaging.messages import org.session.libsession.messaging.MessagingModuleConfiguration -import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.utilities.Address import org.session.libsession.utilities.GroupUtil -import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.toHexString sealed class Destination { @@ -38,16 +36,7 @@ sealed class Destination { fun from(address: Address): Destination { return when { address.isContact -> { - val contact = address.contactIdentifier() - if (SodiumUtilities.SessionId(contact).prefix == IdPrefix.BLINDED) { - OpenGroupInbox( - server = TODO(), - serverPublicKey = TODO(), - blinkedPublicKey = contact - ) - } else { - Contact(address.contactIdentifier()) - } + Contact(address.contactIdentifier()) } address.isClosedGroup -> { val groupID = address.toGroupString() @@ -63,6 +52,14 @@ sealed class Destination { else -> throw Exception("Missing open group for thread with ID: $threadID.") } } + address.isOpenGroupInbox -> { + val groupInboxId = GroupUtil.getDecodedGroupID(address.serialize()).split(".") + OpenGroupInbox( + groupInboxId.dropLast(2).joinToString("."), + groupInboxId.dropLast(1).last(), + groupInboxId.last() + ) + } else -> { throw Exception("TODO: Handle legacy closed groups.") } diff --git a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt index 039291d962..91ee9e107b 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/open_groups/OpenGroupApi.kt @@ -20,6 +20,7 @@ import okhttp3.MediaType import okhttp3.RequestBody import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.sending_receiving.pollers.OpenGroupPoller.Companion.maxInactivityPeriod +import org.session.libsession.messaging.utilities.SessionId import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.snode.OnionRequestAPI import org.session.libsession.snode.OnionResponse @@ -267,7 +268,7 @@ object OpenGroupApi { .plus(bodyHash) if (serverCapabilities.contains("blind")) { SodiumUtilities.blindedKeyPair(publicKey, ed25519KeyPair)?.let { keyPair -> - pubKey = SodiumUtilities.SessionId( + pubKey = SessionId( IdPrefix.BLINDED, keyPair.publicKey.asBytes ).hexString @@ -280,7 +281,7 @@ object OpenGroupApi { ) ?: return Promise.ofFail(Error.SigningFailed) } ?: return Promise.ofFail(Error.SigningFailed) } else { - pubKey = SodiumUtilities.SessionId( + pubKey = SessionId( IdPrefix.UN_BLINDED, ed25519KeyPair.publicKey.asBytes ).hexString diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageDecrypter.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageDecrypter.kt index da9d080aed..6ef1bb0ef0 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageDecrypter.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageDecrypter.kt @@ -5,6 +5,7 @@ import com.goterl.lazysodium.LazySodiumAndroid import com.goterl.lazysodium.SodiumAndroid import com.goterl.lazysodium.interfaces.Box import com.goterl.lazysodium.interfaces.Sign +import org.session.libsession.messaging.utilities.SessionId import org.session.libsignal.crypto.ecc.ECKeyPair import org.session.libsignal.utilities.Hex import org.session.libsignal.utilities.hexEncodedPublicKey @@ -55,6 +56,7 @@ object MessageDecrypter { val senderX25519PublicKey = ByteArray(Sign.CURVE25519_PUBLICKEYBYTES) sodium.convertPublicKeyEd25519ToCurve25519(senderX25519PublicKey, senderED25519PublicKey) - return Pair(plaintext, "05" + senderX25519PublicKey.toHexString()) + val id = SessionId(IdPrefix.STANDARD, senderX25519PublicKey) + return Pair(plaintext, id.hexString) } } \ No newline at end of file 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 dd766ca743..ce3952d7c4 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 @@ -16,6 +16,8 @@ import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.messaging.open_groups.OpenGroupMessage import org.session.libsession.messaging.utilities.MessageWrapper +import org.session.libsession.messaging.utilities.SessionId +import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.snode.RawResponsePromise import org.session.libsession.snode.SnodeAPI import org.session.libsession.snode.SnodeMessage @@ -26,6 +28,7 @@ import org.session.libsession.utilities.SSKEnvironment import org.session.libsignal.crypto.PushTransportDetails import org.session.libsignal.protos.SignalServiceProtos import org.session.libsignal.utilities.Base64 +import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.hexEncodedPublicKey import java.util.concurrent.TimeUnit @@ -56,7 +59,7 @@ object MessageSender { // Convenience fun send(message: Message, destination: Destination): Promise { - return if (destination is Destination.LegacyOpenGroup || destination is Destination.OpenGroup) { + return if (destination is Destination.LegacyOpenGroup || destination is Destination.OpenGroup || destination is Destination.OpenGroupInbox) { sendToOpenGroupDestination(destination, message) } else { sendToSnodeDestination(destination, message) @@ -201,7 +204,14 @@ object MessageSender { if (message.sentTimestamp == null) { message.sentTimestamp = System.currentTimeMillis() } - message.sender = storage.getUserPublicKey() + val openGroup = storage.getOpenGroup(message.threadID!!) + val userEdKeyPair = MessagingModuleConfiguration.shared.getUserED25519KeyPair()!! + val blindedKeyPair = SodiumUtilities.blindedKeyPair(openGroup?.publicKey!!, userEdKeyPair) + message.sender = if (openGroup.capabilities.contains("blind") && blindedKeyPair != null) { + SessionId(IdPrefix.BLINDED, blindedKeyPair.publicKey.asBytes).hexString + } else { + SessionId(IdPrefix.UN_BLINDED, userEdKeyPair.publicKey.asBytes).hexString + } // Set the failure handler (need it here already for precondition failure handling) fun handleFailure(error: Exception) { handleFailedMessageSend(message, error) diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/SodiumUtilities.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/SodiumUtilities.kt index ea44a90353..a11f42484c 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/SodiumUtilities.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/SodiumUtilities.kt @@ -181,22 +181,28 @@ object SodiumUtilities { SessionId(IdPrefix.BLINDED, pk2).publicKey == blindedId.publicKey } - class SessionId { - var prefix: IdPrefix? - var publicKey: String +} - constructor(id: String) { - prefix = IdPrefix.fromValue(id) - publicKey = id.drop(2) - } +class SessionId { + var prefix: IdPrefix? + var publicKey: String - constructor(prefix: IdPrefix, publicKey: ByteArray) { - this.prefix = prefix - this.publicKey = publicKey.toHexString() - } - - val hexString - get() = prefix?.value + publicKey + constructor(id: String) { + prefix = IdPrefix.fromValue(id) + publicKey = id.drop(2) } + constructor(prefix: IdPrefix, publicKey: ByteArray) { + this.prefix = prefix + this.publicKey = publicKey.toHexString() + } + + val hexString + get() = prefix?.value + publicKey } + +data class BlindedIdMapping( + val blindedId: String, + val sessionId: String, + val serverPublicKey: String +) diff --git a/libsession/src/main/java/org/session/libsession/utilities/Address.kt b/libsession/src/main/java/org/session/libsession/utilities/Address.kt index 3278907bd7..baa0ea9a76 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/Address.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/Address.kt @@ -25,6 +25,8 @@ class Address private constructor(address: String) : Parcelable, Comparable