diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt index 3dcdcab1ce..89ed36d909 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt @@ -93,20 +93,21 @@ object MessageReceiver { } } groupPublicKey = envelope.source - try { - decrypt() - } catch(error: Exception) { - val now = System.currentTimeMillis() - var shouldRequestEncryptionKeyPair = true - lastEncryptionKeyPairRequest[groupPublicKey!!]?.let { - shouldRequestEncryptionKeyPair = now - it > 30 * 1000 - } - if (shouldRequestEncryptionKeyPair) { - MessageSender.requestEncryptionKeyPair(groupPublicKey) - lastEncryptionKeyPairRequest[groupPublicKey] = now - } - throw error - } + decrypt() +// try { +// decrypt() +// } catch(error: Exception) { +// val now = System.currentTimeMillis() +// var shouldRequestEncryptionKeyPair = true +// lastEncryptionKeyPairRequest[groupPublicKey!!]?.let { +// shouldRequestEncryptionKeyPair = now - it > 30 * 1000 +// } +// if (shouldRequestEncryptionKeyPair) { +// MessageSender.requestEncryptionKeyPair(groupPublicKey) +// lastEncryptionKeyPairRequest[groupPublicKey] = now +// } +// throw error +// } } else -> throw Error.UnknownEnvelopeType } 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 b90abc9ea2..cf37aa9347 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 @@ -379,12 +379,16 @@ private fun MessageReceiver.handleClosedGroupMembersAdded(message: ClosedGroupCo val members = group.members.map { it.serialize() } val admins = group.admins.map { it.serialize() } - // Users that are part of this remove update val updateMembers = kind.members.map { it.toByteArray().toHexString() } - // newMembers to save is old members minus removed members 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) + } + } storage.insertIncomingInfoMessage(context, senderPublicKey, groupID, SignalServiceProtos.GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) } 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 8562f1ae25..b883d6a922 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 @@ -243,6 +243,7 @@ fun MessageSender.generateAndSendNewEncryptionKeyPair(groupPublicKey: String, ta } } +/// Note: Shouldn't currently be in use. fun MessageSender.requestEncryptionKeyPair(groupPublicKey: String) { val storage = MessagingConfiguration.shared.storage val groupID = GroupUtil.doubleEncodeGroupID(groupPublicKey) @@ -257,149 +258,16 @@ fun MessageSender.requestEncryptionKeyPair(groupPublicKey: String) { send(closedGroupControlMessage, Address.fromSerialized(groupID)) } -/// - Note: Deprecated. -fun MessageSender.update(groupPublicKey: String, members: Collection, name: String): Promise { - val deferred = deferred() - val context = MessagingConfiguration.shared.context +fun MessageSender.sendLatestEncryptionKeyPair(publicKey: String, groupPublicKey: String) { val storage = MessagingConfiguration.shared.storage - val userPublicKey = storage.getUserPublicKey()!! - val sskDatabase = MessagingConfiguration.shared.sskDatabase - val groupID = GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) // double encoded - val group = storage.getGroup(groupID) - if (group == null) { - Log.d("Loki", "Can't update nonexistent closed group.") - deferred.reject(Error.NoThread) - return deferred.promise + 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 oldMembers = group.members.map { it.serialize() }.toSet() - val newMembers = members.minus(oldMembers) - val membersAsData = members.map { Hex.fromStringCondensed(it) } - val admins = group.admins.map { it.serialize() } - val adminsAsData = admins.map { Hex.fromStringCondensed(it) } - val groupPrivateKey = sskDatabase.getClosedGroupPrivateKey(groupPublicKey) - if (groupPrivateKey == null) { - Log.d("Loki", "Couldn't get private key for closed group.") - deferred.reject(Error.NoPrivateKey) - return deferred.promise - } - val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() - val removedMembers = oldMembers.minus(members) - val isUserLeaving = removedMembers.contains(userPublicKey) - val newSenderKeys: List - if (wasAnyUserRemoved) { - if (isUserLeaving && removedMembers.count() != 1) { - Log.d("Loki", "Can't remove self and others simultaneously.") - deferred.reject(Error.InvalidClosedGroupUpdate) - return deferred.promise - } - // Send the update to the existing members using established channels (don't include new ratchets as everyone should regenerate new ratchets individually) - val promises = oldMembers.map { member -> - val closedGroupUpdateKind = ClosedGroupUpdate.Kind.Info(Hex.fromStringCondensed(groupPublicKey), - name, setOf(), membersAsData, adminsAsData) - val closedGroupUpdate = ClosedGroupUpdate() - closedGroupUpdate.kind = closedGroupUpdateKind - val address = Address.fromSerialized(member) - MessageSender.sendNonDurably(closedGroupUpdate, address).get() - } - - val allOldRatchets = sskDatabase.getAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - for (pair in allOldRatchets) { - val senderPublicKey = pair.first - val ratchet = pair.second - val collection = ClosedGroupRatchetCollectionType.Old - sskDatabase.setClosedGroupRatchet(groupPublicKey, senderPublicKey, ratchet, collection) - } - // Delete all ratchets (it's important that this happens * after * sending out the update) - sskDatabase.removeAllClosedGroupRatchets(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - // Remove the group from the user's set of public keys to poll for if the user is leaving. Otherwise generate a new ratchet and - // send it out to all members (minus the removed ones) using established channels. - if (isUserLeaving) { - sskDatabase.removeClosedGroupPrivateKey(groupPublicKey) - storage.setActive(groupID, false) - storage.removeMember(groupID, Address.fromSerialized(userPublicKey)) - // Notify the PN server - PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) - } else { - // Send closed group update messages to any new members using established channels - for (member in newMembers) { - val closedGroupUpdateKind = ClosedGroupUpdate.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, - Hex.fromStringCondensed(groupPrivateKey), listOf(), membersAsData, adminsAsData) - val closedGroupUpdate = ClosedGroupUpdate() - closedGroupUpdate.kind = closedGroupUpdateKind - val address = Address.fromSerialized(member) - MessageSender.sendNonDurably(closedGroupUpdate, address) - } - // Send out the user's new ratchet to all members (minus the removed ones) using established channels - val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey) - val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey)) - for (member in members) { - if (member == userPublicKey) { continue } - val closedGroupUpdateKind = ClosedGroupUpdate.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey) - val closedGroupUpdate = ClosedGroupUpdate() - closedGroupUpdate.kind = closedGroupUpdateKind - val address = Address.fromSerialized(member) - MessageSender.sendNonDurably(closedGroupUpdate, address) - } - } - } else if (newMembers.isNotEmpty()) { - // Generate ratchets for any new members - newSenderKeys = newMembers.map { publicKey -> - val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey) - ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) - } - // Send a closed group update message to the existing members with the new members' ratchets (this message is aimed at the group) - val closedGroupUpdateKind = ClosedGroupUpdate.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, - newSenderKeys, membersAsData, adminsAsData) - val closedGroupUpdate = ClosedGroupUpdate() - closedGroupUpdate.kind = closedGroupUpdateKind - val address = Address.fromSerialized(groupID) - MessageSender.send(closedGroupUpdate, address) - // Send closed group update messages to the new members using established channels - var allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - allSenderKeys = allSenderKeys.union(newSenderKeys) - for (member in newMembers) { - val closedGroupUpdateKind = ClosedGroupUpdate.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, - Hex.fromStringCondensed(groupPrivateKey), allSenderKeys, membersAsData, adminsAsData) - val closedGroupUpdate = ClosedGroupUpdate() - closedGroupUpdate.kind = closedGroupUpdateKind - val address = Address.fromSerialized(member) - MessageSender.send(closedGroupUpdate, address) - } - } else { - val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey, ClosedGroupRatchetCollectionType.Current) - val closedGroupUpdateKind = ClosedGroupUpdate.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, - allSenderKeys, membersAsData, adminsAsData) - val closedGroupUpdate = ClosedGroupUpdate() - closedGroupUpdate.kind = closedGroupUpdateKind - val address = Address.fromSerialized(groupID) - MessageSender.send(closedGroupUpdate, address) - } - // Update the group - storage.updateTitle(groupID, name) - if (!isUserLeaving) { - // The call below sets isActive to true, so if the user is leaving we have to use groupDB.remove(...) instead - storage.updateMembers(groupID, members.map { Address.fromSerialized(it) }) - } - // Notify the user - val infoType = if (isUserLeaving) SignalServiceProtos.GroupContext.Type.QUIT else SignalServiceProtos.GroupContext.Type.UPDATE - val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(groupID)) - storage.insertOutgoingInfoMessage(context, groupID, infoType, name, members, admins, threadID) - deferred.resolve(Unit) - return deferred.promise -} - -/// - Note: Deprecated. -fun MessageSender.leave(groupPublicKey: String) { - val storage = MessagingConfiguration.shared.storage - val userPublicKey = storage.getUserPublicKey()!! - val groupID = GroupUtil.getEncodedClosedGroupID(GroupUtil.getEncodedClosedGroupID(Hex.fromStringCondensed(groupPublicKey)).toByteArray()) // double encoded - val group = storage.getGroup(groupID) - if (group == null) { - Log.d("Loki", "Can't leave nonexistent closed group.") + 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 } - val name = group.title - val oldMembers = group.members.map { it.serialize() }.toSet() - val newMembers = oldMembers.minus(userPublicKey) - return update(groupPublicKey, newMembers, name).get() } \ No newline at end of file