diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt index 6833eca2f8..114b0ba326 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiverHandler.kt @@ -315,7 +315,7 @@ private fun MessageReceiver.handleClosedGroupEncryptionKeyPair(message: ClosedGr return } if (!group.members.map { it.toString() }.contains(senderPublicKey)) { - android.util.Log.d("Loki", "Ignoring closed group encryption key pair from non-member.") + Log.d("Loki", "Ignoring closed group encryption key pair from non-member.") return } // Find our wrapper and decrypt it if possible @@ -362,6 +362,7 @@ private fun MessageReceiver.handleClosedGroupNameChanged(message: ClosedGroupCon private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupControlMessage) { val context = MessagingConfiguration.shared.context val storage = MessagingConfiguration.shared.storage + val userPublicKey = storage.getUserPublicKey()!! val senderPublicKey = message.sender ?: return val kind = message.kind!! as? ClosedGroupControlMessage.Kind.MembersAdded ?: return val groupPublicKey = message.groupPublicKey ?: return @@ -370,9 +371,7 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo Log.d("Loki", "Ignoring closed group info message for nonexistent group.") return } - if (!isValidGroupUpdate(group, message.sentTimestamp!!, senderPublicKey)) { - return - } + if (!isValidGroupUpdate(group, message.sentTimestamp!!, senderPublicKey)) { return } val name = group.title // Check common group update logic val members = group.members.map { it.serialize() } @@ -381,11 +380,22 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo val updateMembers = kind.members.map { it.toByteArray().toHexString() } val newMembers = members + updateMembers storage.updateMembers(groupID, newMembers.map { Address.fromSerialized(it) }) - // Send the latest encryption key pair to the added members if the current user is the admin of the group - val isCurrentUserAdmin = admins.contains(storage.getUserPublicKey()!!) - if (isCurrentUserAdmin) { - for (member in updateMembers) { - MessageSender.sendLatestEncryptionKeyPair(member, groupPublicKey) + if (userPublicKey == senderPublicKey) { + val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) + storage.insertOutgoingInfoMessage(context, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, name, members, admins, threadID, message.sentTimestamp!!) + } else { + storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, message.sentTimestamp!!) + } + if (userPublicKey in admins) { + // send current encryption key to the latest added members + val encryptionKeyPair = pendingKeyPair[groupPublicKey]?.orNull() + ?: storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) + if (encryptionKeyPair == null) { + android.util.Log.d("Loki", "Couldn't get encryption key pair for closed group.") + } else { + for (user in updateMembers) { + MessageSender.sendEncryptionKeyPair(groupPublicKey, encryptionKeyPair, setOf(user), targetUser = user, force = false) + } } } storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, message.sentTimestamp!!) @@ -491,7 +501,13 @@ private fun MessageReceiver.handleClosedGroupEncryptionKeyPairRequest(message: C return } if (!isValidGroupUpdate(group, message.sentTimestamp!!, senderPublicKey)) { return } - MessageSender.sendLatestEncryptionKeyPair(senderPublicKey, groupPublicKey) + val encryptionKeyPair = pendingKeyPair[groupPublicKey]?.orNull() + ?: storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) + if (encryptionKeyPair == null) { + Log.d("Loki", "Couldn't get encryption key pair for closed group.") + } else { + MessageSender.sendEncryptionKeyPair(groupPublicKey, encryptionKeyPair, setOf(senderPublicKey), targetUser = senderPublicKey, force = false) + } } private fun isValidGroupUpdate(group: GroupRecord, diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt index 2a5ffe2a50..fc541aace8 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSenderClosedGroup.kt @@ -2,7 +2,9 @@ package org.session.libsession.messaging.sending_receiving +import android.content.Context import com.google.protobuf.ByteString +import com.google.protobuf.Message import nl.komponents.kovenant.Promise import nl.komponents.kovenant.deferred @@ -27,7 +29,7 @@ import org.session.libsignal.utilities.logging.Log import java.util.* import java.util.concurrent.ConcurrentHashMap -private val pendingKeyPair = ConcurrentHashMap>() +val pendingKeyPair = ConcurrentHashMap>() fun MessageSender.create(name: String, members: Collection): Promise { val deferred = deferred() @@ -247,6 +249,15 @@ fun MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey: String, ta // make sure we set the pendingKeyPair or wait until it is not null } while (!pendingKeyPair.replace(groupPublicKey,Optional.absent(),Optional.fromNullable(newKeyPair))) // Distribute it + sendEncryptionKeyPair(groupPublicKey, newKeyPair, targetMembers)?.success { + // Store it * after * having sent out the message to the group + storage.addClosedGroupEncryptionKeyPair(newKeyPair, groupPublicKey) + pendingKeyPair[groupPublicKey] = Optional.absent() + } +} + +fun MessageSender.sendEncryptionKeyPair(groupPublicKey: String, newKeyPair: ECKeyPair, targetMembers: Collection, targetUser: String? = null, force: Boolean = true): Promise? { + val destination = targetUser ?: GroupUtil.doubleEncodeGroupID(groupPublicKey) val proto = SignalServiceProtos.KeyPair.newBuilder() proto.publicKey = ByteString.copyFrom(newKeyPair.publicKey.serialize().removing05PrefixIfNeeded()) proto.privateKey = ByteString.copyFrom(newKeyPair.privateKey.serialize()) @@ -255,14 +266,15 @@ fun MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey: String, ta val ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, publicKey) ClosedGroupControlMessage.KeyPairWrapper(publicKey, ByteString.copyFrom(ciphertext)) } - val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(null, wrappers) + val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), wrappers) val sentTime = System.currentTimeMillis() val closedGroupControlMessage = ClosedGroupControlMessage(kind) closedGroupControlMessage.sentTimestamp = sentTime - sendNonDurably(closedGroupControlMessage, Address.fromSerialized(groupID)).success { - // Store it * after * having sent out the message to the group - storage.addClosedGroupEncryptionKeyPair(newKeyPair, groupPublicKey) - pendingKeyPair[groupPublicKey] = Optional.absent() + return if (force) { + MessageSender.sendNonDurably(closedGroupControlMessage, Address.fromSerialized(destination)) + } else { + MessageSender.send(closedGroupControlMessage, Address.fromSerialized(destination)) + null } } @@ -281,34 +293,4 @@ fun MessageSender.requestEncryptionKeyPair(groupPublicKey: String) { val closedGroupControlMessage = ClosedGroupControlMessage(ClosedGroupControlMessage.Kind.EncryptionKeyPairRequest()) closedGroupControlMessage.sentTimestamp = sentTime send(closedGroupControlMessage, Address.fromSerialized(groupID)) -} - -fun MessageSender.sendLatestEncryptionKeyPair(publicKey: String, groupPublicKey: String) { - val storage = MessagingConfiguration.shared.storage - val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey) - val group = storage.getGroup(groupID) ?: run { - Log.d("Loki", "Can't send encryption key pair for nonexistent closed group.") - throw Error.NoThread - } - val members = group.members.map { it.serialize() } - if (!members.contains(publicKey)) { - Log.d("Loki", "Refusing to send latest encryption key pair to non-member.") - return - } - // Get the latest encryption key pair - val encryptionKeyPair = pendingKeyPair[groupPublicKey]?.orNull() - ?: storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: return - // Send it - val proto = SignalServiceProtos.KeyPair.newBuilder() - proto.publicKey = ByteString.copyFrom(encryptionKeyPair.publicKey.serialize()) - proto.privateKey = ByteString.copyFrom(encryptionKeyPair.privateKey.serialize()) - val plaintext = proto.build().toByteArray() - val ciphertext = MessageSenderEncryption.encryptWithSessionProtocol(plaintext, publicKey) - Log.d("Loki", "Sending latest encryption key pair to: $publicKey.") - val wrapper = ClosedGroupControlMessage.KeyPairWrapper(publicKey, ByteString.copyFrom(ciphertext)) - val kind = ClosedGroupControlMessage.Kind.EncryptionKeyPair(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), listOf(wrapper)) - val sentTime = System.currentTimeMillis() - val closedGroupControlMessage = ClosedGroupControlMessage(kind) - closedGroupControlMessage.sentTimestamp = sentTime - MessageSender.send(closedGroupControlMessage, Address.fromSerialized(publicKey)) } \ No newline at end of file