From 1e16a940fe2bd697fc9c25b672fe2941c0d37e5e Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Mon, 10 Aug 2020 09:23:51 +1000 Subject: [PATCH] Clean & debug --- .../loki/protocol/ClosedGroupsProtocol.kt | 60 ++++++++++++------- 1 file changed, 38 insertions(+), 22 deletions(-) diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index db52522d69..f44250b994 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -9,7 +9,6 @@ import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.loki.utilities.recipient import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.sms.MessageSender -import org.thoughtcrime.securesms.sms.OutgoingTextMessage import org.thoughtcrime.securesms.util.GroupUtil import org.thoughtcrime.securesms.util.Hex import org.thoughtcrime.securesms.util.TextSecurePreferences @@ -39,14 +38,14 @@ object ClosedGroupsProtocol { } // Create the group val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) - val admins = setOf( Address.fromSerialized(userPublicKey) ) + val admins = setOf( userPublicKey ) DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), - null, null, LinkedList
(admins)) + null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, members) // Send a closed group update message to all members using established channels - val adminsAsData = admins.map { Hex.fromStringCondensed(it.serialize()) } + val adminsAsData = admins.map { Hex.fromStringCondensed(it) } val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, groupKeyPair.privateKey.serialize(), senderKeys, membersAsData, adminsAsData) for (member in members) { @@ -64,6 +63,7 @@ object ClosedGroupsProtocol { public fun addMembers(context: Context, newMembers: Collection, groupPublicKey: String) { // Prepare + val sskDatabase = DatabaseFactory.getSSKDatabase(context) val groupDB = DatabaseFactory.getGroupDatabase(context) val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) val group = groupDB.getGroup(groupID).orNull() @@ -73,8 +73,8 @@ object ClosedGroupsProtocol { } val name = group.title val admins = group.admins.map { Hex.fromStringCondensed(it.serialize()) } - val privateKey = DatabaseFactory.getSSKDatabase(context).getClosedGroupPrivateKey(groupPublicKey) - if (privateKey == null) { + val groupPrivateKey = DatabaseFactory.getSSKDatabase(context).getClosedGroupPrivateKey(groupPublicKey) + if (groupPrivateKey == null) { Log.d("Loki", "Couldn't get private key for closed group.") return } @@ -95,10 +95,11 @@ object ClosedGroupsProtocol { // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, newMembers) // Send closed group update messages to the new members using established channels + val allSenderKeys = sskDatabase.getAllClosedGroupSenderKeys(groupPublicKey) + senderKeys for (member in members) { @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.Info(Hex.fromStringCondensed(groupPublicKey), name, - senderKeys, membersAsData, admins) + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, + Hex.fromStringCondensed(groupPrivateKey), allSenderKeys, membersAsData, admins) @Suppress("NAME_SHADOWING") val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) ApplicationContext.getInstance(context).jobManager.add(job) @@ -120,6 +121,7 @@ object ClosedGroupsProtocol { val isUserLeaving = membersToRemove.contains(userPublicKey) if (isUserLeaving && membersToRemove.count() != 1) { Log.d("Loki", "Can't remove self and others simultaneously.") + return } val groupDB = DatabaseFactory.getGroupDatabase(context) val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) @@ -164,12 +166,21 @@ object ClosedGroupsProtocol { // TODO: Implement } - public fun handleSharedSenderKeysUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate) { + public fun requestSenderKey(context: Context, groupPublicKey: String, senderPublicKey: String) { + // Establish session if needed + ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey) + // Send the request + val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKeyRequest(Hex.fromStringCondensed(groupPublicKey)) + val job = ClosedGroupUpdateMessageSendJob(senderPublicKey, closedGroupUpdateKind) + ApplicationContext.getInstance(context).jobManager.add(job) + } + + public fun handleSharedSenderKeysUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { when (closedGroupUpdate.type) { SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> handleNewClosedGroup(context, closedGroupUpdate) - SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> handleClosedGroupUpdate(context, closedGroupUpdate) - SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST -> handleSenderKeyRequest(context, closedGroupUpdate) - SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY -> handleSenderKey(context, closedGroupUpdate) + SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> handleClosedGroupUpdate(context, closedGroupUpdate, senderPublicKey) + SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY_REQUEST -> handleSenderKeyRequest(context, closedGroupUpdate, senderPublicKey) + SignalServiceProtos.ClosedGroupUpdate.Type.SENDER_KEY -> handleSenderKey(context, closedGroupUpdate, senderPublicKey) else -> { // Do nothing } @@ -190,6 +201,7 @@ object ClosedGroupsProtocol { val admins = closedGroupUpdate.adminsList.map { it.toByteArray().toHexString() } // Persist the ratchets senderKeys.forEach { senderKey -> + if (!members.contains(senderKey.publicKey.toHexString())) { return@forEach } val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet) } @@ -206,7 +218,7 @@ object ClosedGroupsProtocol { establishSessionsWithMembersIfNeeded(context, members) } - public fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate) { + public fun handleClosedGroupUpdate(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { // Prepare val userPublicKey = TextSecurePreferences.getLocalNumber(context) val sskDatabase = DatabaseFactory.getSSKDatabase(context) @@ -221,18 +233,18 @@ object ClosedGroupsProtocol { val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) val group = groupDB.getGroup(groupID).orNull() if (group == null) { - Log.d("Loki", "Ignoring closed group update for nonexistent group.") + Log.d("Loki", "Ignoring closed group info message for nonexistent group.") return } val oldMembers = group.members.map { it.serialize() } - val senderPublicKey = "" // TODO // Check that the sender is a member of the group (before the update) if (!oldMembers.contains(senderPublicKey)) { Log.d("Loki", "Ignoring closed group info message from non-member.") + return } // Store the ratchets for any new members (it's important that this happens before the code below) senderKeys.forEach { senderKey -> - // TODO: Ignore sender keys if the public key they specify isn't a member of the group + if (!members.contains(senderKey.publicKey.toHexString())) { return@forEach } val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet) } @@ -262,7 +274,7 @@ object ClosedGroupsProtocol { // TODO: Implement } - public fun handleSenderKeyRequest(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate) { + public fun handleSenderKeyRequest(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { // Prepare val userPublicKey = TextSecurePreferences.getLocalNumber(context) val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() @@ -270,13 +282,13 @@ object ClosedGroupsProtocol { val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) val group = groupDB.getGroup(groupID).orNull() if (group == null) { - Log.d("Loki", "Ignoring sender key request for nonexistent group.") + Log.d("Loki", "Ignoring closed group sender key request for nonexistent group.") return } // Check that the requesting user is a member of the group - val senderPublicKey = "" if (!group.members.map { it.serialize() }.contains(senderPublicKey)) { - Log.d("Loki", "Ignoring sender key request from non-member.") + Log.d("Loki", "Ignoring closed group sender key request from non-member.") + return } // Respond to the request ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(senderPublicKey) @@ -287,7 +299,7 @@ object ClosedGroupsProtocol { ApplicationContext.getInstance(context).jobManager.add(job) } - public fun handleSenderKey(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate) { + public fun handleSenderKey(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { // Prepare val sskDatabase = DatabaseFactory.getSSKDatabase(context) val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() @@ -304,10 +316,14 @@ object ClosedGroupsProtocol { return } val senderKey = ClosedGroupSenderKey(senderKeyProto.chainKey.toByteArray(), senderKeyProto.keyIndex, senderKeyProto.publicKey.toByteArray()) - val senderPublicKey = senderKeyProto.publicKey.toByteArray().toHexString() // Check that the sending user is a member of the group if (!group.members.map { it.serialize() }.contains(senderPublicKey)) { Log.d("Loki", "Ignoring closed group sender key from non-member.") + return + } + if (senderKeyProto.publicKey.toByteArray().toHexString() != senderPublicKey) { + Log.d("Loki", "Ignoring invalid closed group sender key.") + return } // Store the sender key val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf())