Open group message requests

This commit is contained in:
ceokot 2022-05-03 15:23:58 +10:00
parent d4fb77c695
commit a24225cf8b
10 changed files with 114 additions and 35 deletions

View File

@ -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>(ADDRESS)?.let { address ->
intent.getParcelableExtra<Address>(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

View File

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

View File

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

View File

@ -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,17 +36,8 @@ 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())
}
}
address.isClosedGroup -> {
val groupID = address.toGroupString()
val groupPublicKey = GroupUtil.doubleDecodeGroupID(groupID).toHexString()
@ -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.")
}

View File

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

View File

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

View File

@ -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<Unit, Exception> {
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)

View File

@ -181,6 +181,8 @@ object SodiumUtilities {
SessionId(IdPrefix.BLINDED, pk2).publicKey == blindedId.publicKey
}
}
class SessionId {
var prefix: IdPrefix?
var publicKey: String
@ -199,4 +201,8 @@ object SodiumUtilities {
get() = prefix?.value + publicKey
}
}
data class BlindedIdMapping(
val blindedId: String,
val sessionId: String,
val serverPublicKey: String
)

View File

@ -25,6 +25,8 @@ class Address private constructor(address: String) : Parcelable, Comparable<Addr
get() = GroupUtil.isClosedGroup(address)
val isOpenGroup: Boolean
get() = GroupUtil.isOpenGroup(address)
val isOpenGroupInbox: Boolean
get() = GroupUtil.isOpenGroupInbox(address)
val isContact: Boolean
get() = !isGroup

View File

@ -8,12 +8,18 @@ import kotlin.jvm.Throws
object GroupUtil {
const val CLOSED_GROUP_PREFIX = "__textsecure_group__!"
const val OPEN_GROUP_PREFIX = "__loki_public_chat_group__!"
const val OPEN_GROUP_INBOX_PREFIX = "__loki_public_chat_group_inbox__!"
@JvmStatic
fun getEncodedOpenGroupID(groupID: ByteArray): String {
return OPEN_GROUP_PREFIX + Hex.toStringCondensed(groupID)
}
@JvmStatic
fun getEncodedOpenGroupInboxID(groupInboxID: ByteArray): String {
return OPEN_GROUP_INBOX_PREFIX + Hex.toStringCondensed(groupInboxID)
}
@JvmStatic
fun getEncodedClosedGroupID(groupID: ByteArray): String {
return CLOSED_GROUP_PREFIX + Hex.toStringCondensed(groupID)
@ -46,7 +52,8 @@ object GroupUtil {
}
fun isEncodedGroup(groupId: String): Boolean {
return groupId.startsWith(CLOSED_GROUP_PREFIX) || groupId.startsWith(OPEN_GROUP_PREFIX)
return groupId.startsWith(CLOSED_GROUP_PREFIX) || groupId.startsWith(OPEN_GROUP_PREFIX) ||
groupId.startsWith(OPEN_GROUP_INBOX_PREFIX)
}
@JvmStatic
@ -54,6 +61,11 @@ object GroupUtil {
return groupId.startsWith(OPEN_GROUP_PREFIX)
}
@JvmStatic
fun isOpenGroupInbox(groupId: String): Boolean {
return groupId.startsWith(OPEN_GROUP_INBOX_PREFIX)
}
@JvmStatic
fun isClosedGroup(groupId: String): Boolean {
return groupId.startsWith(CLOSED_GROUP_PREFIX)