diff --git a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 024b9a5bcc..a26b7a2398 100644 --- a/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/src/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -209,7 +209,6 @@ import org.thoughtcrime.securesms.util.Dialogs; import org.thoughtcrime.securesms.util.DynamicLanguage; import org.thoughtcrime.securesms.util.DynamicNoActionBarTheme; import org.thoughtcrime.securesms.util.ExpirationUtil; -import org.thoughtcrime.securesms.util.GroupUtil; import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.ServiceUtil; @@ -1171,7 +1170,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity String groupPublicKey; boolean isSSKBasedClosedGroup; try { - groupPublicKey = HexEncodingKt.toHexString(GroupUtil.getDecodedId(groupRecipient.getAddress().toString())); + groupPublicKey = HexEncodingKt.toHexString(ClosedGroupsProtocol.doubleDecodeGroupID(groupRecipient.getAddress().toString())); isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey); } catch (IOException e) { groupPublicKey = null; diff --git a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt index 0b03d16933..3cdae8382b 100644 --- a/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt +++ b/src/org/thoughtcrime/securesms/loki/activities/HomeActivity.kt @@ -335,7 +335,7 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe val isClosedGroup = recipient.address.isClosedGroup // Send a leave group message if this is an active closed group if (isClosedGroup && DatabaseFactory.getGroupDatabase(this).isActive(recipient.address.toGroupString())) { - val groupPublicKey = GroupUtil.getDecodedId(recipient.address.toString()).toHexString() + val groupPublicKey = ClosedGroupsProtocol.doubleDecodeGroupID(recipient.address.toString()).toHexString() val isSSKBasedClosedGroup = DatabaseFactory.getSSKDatabase(this).isSSKBasedClosedGroup(groupPublicKey) if (isSSKBasedClosedGroup) { ClosedGroupsProtocol.leave(this, groupPublicKey) diff --git a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt index d43492b48b..7eb0f694da 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocol.kt @@ -27,10 +27,11 @@ import org.whispersystems.signalservice.loki.protocol.closedgroups.SharedSenderK import org.whispersystems.signalservice.loki.utilities.hexEncodedPrivateKey import org.whispersystems.signalservice.loki.utilities.hexEncodedPublicKey import org.whispersystems.signalservice.loki.utilities.toHexString +import java.io.IOException import java.util.* object ClosedGroupsProtocol { - val isSharedSenderKeysEnabled = false + val isSharedSenderKeysEnabled = true public fun createClosedGroup(context: Context, name: String, members: Collection): String { // Prepare @@ -45,7 +46,7 @@ object ClosedGroupsProtocol { ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) } // Create the group - val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) + val groupID = doubleEncodeGroupID(groupPublicKey) val admins = setOf( userPublicKey ) DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) @@ -74,7 +75,7 @@ object ClosedGroupsProtocol { // Prepare val sskDatabase = DatabaseFactory.getSSKDatabase(context) val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) + val groupID = doubleEncodeGroupID(groupPublicKey) val group = groupDB.getGroup(groupID).orNull() if (group == null) { Log.d("Loki", "Can't add users to nonexistent closed group.") @@ -136,7 +137,7 @@ object ClosedGroupsProtocol { return } val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) + val groupID = doubleEncodeGroupID(groupPublicKey) val group = groupDB.getGroup(groupID).orNull() if (group == null) { Log.d("Loki", "Can't add users to nonexistent closed group.") @@ -241,15 +242,14 @@ object ClosedGroupsProtocol { sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet) } // Create the group - val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) + val groupID = doubleEncodeGroupID(groupPublicKey) DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList
(members.map { Address.fromSerialized(it) }), null, null, LinkedList
(admins.map { Address.fromSerialized(it) })) DatabaseFactory.getRecipientDatabase(context).setProfileSharing(Recipient.from(context, Address.fromSerialized(groupID), false), true) // Add the group to the user's set of public keys to poll for sskDatabase.setClosedGroupPrivateKey(groupPublicKey, groupPrivateKey.toHexString()) // Notify the user - val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertIncomingInfoMessage(context, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins, threadID) + insertIncomingInfoMessage(context, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) // Establish sessions if needed establishSessionsWithMembersIfNeeded(context, members) } @@ -267,7 +267,7 @@ object ClosedGroupsProtocol { val members = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() } val admins = closedGroupUpdate.adminsList.map { it.toByteArray().toHexString() } val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) + val groupID = doubleEncodeGroupID(groupPublicKey) val group = groupDB.getGroup(groupID).orNull() if (group == null) { Log.d("Loki", "Ignoring closed group info message for nonexistent group.") @@ -312,8 +312,7 @@ object ClosedGroupsProtocol { // Notify the user val type0 = if (wasAnyUserRemoved) GroupContext.Type.QUIT else GroupContext.Type.UPDATE val type1 = if (wasAnyUserRemoved) SignalServiceGroup.Type.QUIT else SignalServiceGroup.Type.UPDATE - val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertIncomingInfoMessage(context, groupID, type0, type1, name, members, admins, threadID) + insertIncomingInfoMessage(context, groupID, type0, type1, name, members, admins) } public fun handleSenderKeyRequest(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdate, senderPublicKey: String) { @@ -321,7 +320,7 @@ object ClosedGroupsProtocol { val userPublicKey = TextSecurePreferences.getLocalNumber(context) val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) + val groupID = doubleEncodeGroupID(groupPublicKey) val group = groupDB.getGroup(groupID).orNull() if (group == null) { Log.d("Loki", "Ignoring closed group sender key request for nonexistent group.") @@ -346,7 +345,7 @@ object ClosedGroupsProtocol { val sskDatabase = DatabaseFactory.getSSKDatabase(context) val groupPublicKey = closedGroupUpdate.groupPublicKey.toByteArray().toHexString() val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false) + val groupID = doubleEncodeGroupID(groupPublicKey) val group = groupDB.getGroup(groupID).orNull() if (group == null) { Log.d("Loki", "Ignoring closed group sender key for nonexistent group.") @@ -390,7 +389,7 @@ object ClosedGroupsProtocol { if (GroupUtil.isOpenGroup(groupID)) { return listOf( Address.fromSerialized(groupID) ) } else { - val groupPublicKey = GroupUtil.getDecodedId(groupID).toHexString() + val groupPublicKey = doubleDecodeGroupID(groupID).toHexString() if (DatabaseFactory.getSSKDatabase(context).isSSKBasedClosedGroup(groupPublicKey)) { return listOf( Address.fromSerialized(groupPublicKey) ) } else { @@ -456,7 +455,7 @@ object ClosedGroupsProtocol { } private fun insertIncomingInfoMessage(context: Context, groupID: String, type0: GroupContext.Type, type1: SignalServiceGroup.Type, name: String, - members: Collection, admins: Collection, threadID: Long) { + members: Collection, admins: Collection) { val groupContextBuilder = GroupContext.newBuilder() .setId(ByteString.copyFrom(GroupUtil.getDecodedId(groupID))) .setType(type0) @@ -484,4 +483,18 @@ object ClosedGroupsProtocol { val infoMessageID = mmsDB.insertMessageOutbox(infoMessage, threadID, false, null) mmsDB.markAsSent(infoMessageID, true) } + + // NOTE: Signal group ID handling is weird. The ID is double encoded in the database, but not in a `GroupContext`. + + @JvmStatic + @Throws(IOException::class) + public fun doubleEncodeGroupID(groupPublicKey: String): String { + return GroupUtil.getEncodedId(GroupUtil.getEncodedId(Hex.fromStringCondensed(groupPublicKey), false).toByteArray(), false) + } + + @JvmStatic + @Throws(IOException::class) + public fun doubleDecodeGroupID(groupID: String): ByteArray { + return GroupUtil.getDecodedId(GroupUtil.getDecodedStringId(groupID)) + } } \ No newline at end of file