From c8f1c862fb64690256ca63033df52b26f43e7d79 Mon Sep 17 00:00:00 2001 From: jubb Date: Thu, 18 Feb 2021 11:56:45 +1100 Subject: [PATCH 1/9] refactor: turn inner wrapped group edit calls into synchronous calls to remove unnecessary nesting --- .../activities/EditClosedGroupActivity.kt | 13 +- .../loki/protocol/ClosedGroupsProtocolV2.kt | 221 ++++++++---------- 2 files changed, 100 insertions(+), 134 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt index 80fd0281ab..aff070fdf6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/EditClosedGroupActivity.kt @@ -280,18 +280,15 @@ class EditClosedGroupActivity : PassphraseRequiredActionBarActivity() { ClosedGroupsProtocolV2.explicitLeave(this, groupPublicKey!!) } else { task { - val name = - if (hasNameChanged) ClosedGroupsProtocolV2.explicitNameChange(this@EditClosedGroupActivity,groupPublicKey!!,name) - else Promise.of(Unit) - name.get() + if (hasNameChanged) { + ClosedGroupsProtocolV2.explicitNameChange(this@EditClosedGroupActivity, groupPublicKey!!, name) + } members.filterNot { it in originalMembers }.let { adds -> if (adds.isNotEmpty()) ClosedGroupsProtocolV2.explicitAddMembers(this@EditClosedGroupActivity, groupPublicKey!!, adds.map { it.address.serialize() }) - else Promise.of(Unit) - }.get() + } originalMembers.filterNot { it in members }.let { removes -> if (removes.isNotEmpty()) ClosedGroupsProtocolV2.explicitRemoveMembers(this@EditClosedGroupActivity, groupPublicKey!!, removes.map { it.address.serialize() }) - else Promise.of(Unit) - }.get() + } } } promise.successUi { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index fff03d6b74..0f09ca80e0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -2,6 +2,7 @@ package org.thoughtcrime.securesms.loki.protocol import android.content.Context import android.util.Log +import androidx.annotation.WorkerThread import com.google.protobuf.ByteString import nl.komponents.kovenant.Promise import nl.komponents.kovenant.deferred @@ -129,147 +130,115 @@ object ClosedGroupsProtocolV2 { } @JvmStatic - fun explicitAddMembers(context: Context, groupPublicKey: String, membersToAdd: List): Promise { - return task { - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() - if (group == null) { - Log.d("Loki", "Can't leave nonexistent closed group.") - return@task Error.NoThread - } - val updatedMembers = group.members.map { it.serialize() }.toSet() + membersToAdd - // Save the new group members - groupDB.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) }) - val membersAsData = updatedMembers.map { Hex.fromStringCondensed(it) } - val newMembersAsData = membersToAdd.map { Hex.fromStringCondensed(it) } - val admins = group.admins.map { it.serialize() } - val adminsAsData = admins.map { Hex.fromStringCondensed(it) } - val sentTime = System.currentTimeMillis() - val encryptionKeyPair = pendingKeyPair.getOrElse(groupPublicKey) { - Optional.fromNullable(apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)) - }.orNull() - if (encryptionKeyPair == null) { - Log.d("Loki", "Couldn't get encryption key pair for closed group.") - return@task Error.NoKeyPair - } - val name = group.title - // Send the update to the group - val memberUpdateKind = ClosedGroupUpdateMessageSendJobV2.Kind.AddMembers(newMembersAsData) - val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, memberUpdateKind, sentTime) - job.setContext(context) - job.onRun() // Run the job immediately - // Notify the user - val infoType = GroupContext.Type.UPDATE - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime) - // Send closed group update messages to any new members individually - for (member in membersToAdd) { - @Suppress("NAME_SHADOWING") - val closedGroupNewKind = ClosedGroupUpdateMessageSendJobV2.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, encryptionKeyPair, membersAsData, adminsAsData) - @Suppress("NAME_SHADOWING") - val newMemberJob = ClosedGroupUpdateMessageSendJobV2(member, closedGroupNewKind, sentTime) - ApplicationContext.getInstance(context).jobManager.add(newMemberJob) - } + fun explicitAddMembers(context: Context, groupPublicKey: String, membersToAdd: List) { + val apiDB = DatabaseFactory.getLokiAPIDatabase(context) + val groupDB = DatabaseFactory.getGroupDatabase(context) + val groupID = doubleEncodeGroupID(groupPublicKey) + val group = groupDB.getGroup(groupID).orNull() + if (group == null) { + Log.d("Loki", "Can't leave nonexistent closed group.") + throw Error.NoThread } - } - - @JvmStatic - fun explicitRemoveMembers(context: Context, groupPublicKey: String, membersToRemove: List): Promise { - return task { - val userPublicKey = TextSecurePreferences.getLocalNumber(context) - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() - if (group == null) { - Log.d("Loki", "Can't leave nonexistent closed group.") - return@task Error.NoThread - } - val updatedMembers = group.members.map { it.serialize() }.toSet() - membersToRemove - // Save the new group members - groupDB.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) }) - val removeMembersAsData = membersToRemove.map { Hex.fromStringCondensed(it) } - val admins = group.admins.map { it.serialize() } - val sentTime = System.currentTimeMillis() - val encryptionKeyPair = apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) - if (encryptionKeyPair == null) { - Log.d("Loki", "Couldn't get encryption key pair for closed group.") - return@task Error.NoKeyPair - } - if (membersToRemove.any { it in admins } && updatedMembers.isNotEmpty()) { - Log.d("Loki", "Can't remove admin from closed group unless the group is destroyed entirely.") - return@task Error.InvalidUpdate - } - val name = group.title - // Send the update to the group - val memberUpdateKind = ClosedGroupUpdateMessageSendJobV2.Kind.RemoveMembers(removeMembersAsData) - val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, memberUpdateKind, sentTime) - job.setContext(context) - job.onRun() // Run the job immediately - // Notify the user - val infoType = GroupContext.Type.UPDATE - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime) - val isCurrentUserAdmin = admins.contains(userPublicKey) - if (isCurrentUserAdmin) { - generateAndSendNewEncryptionKeyPair(context, groupPublicKey, updatedMembers) - } - return@task Unit + val updatedMembers = group.members.map { it.serialize() }.toSet() + membersToAdd + val membersAsData = updatedMembers.map { Hex.fromStringCondensed(it) } + val newMembersAsData = membersToAdd.map { Hex.fromStringCondensed(it) } + val admins = group.admins.map { it.serialize() } + val adminsAsData = admins.map { Hex.fromStringCondensed(it) } + val sentTime = System.currentTimeMillis() + val encryptionKeyPair = pendingKeyPair.getOrElse(groupPublicKey) { + Optional.fromNullable(apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey)) + }.orNull() + if (encryptionKeyPair == null) { + Log.d("Loki", "Couldn't get encryption key pair for closed group.") + throw Error.NoKeyPair + } + val name = group.title + // Send the update to the group + val memberUpdateKind = ClosedGroupUpdateMessageSendJobV2.Kind.AddMembers(newMembersAsData) + val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, memberUpdateKind, sentTime) + job.setContext(context) + job.onRun() // Run the job immediately + // Save the new group members + groupDB.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) }) + // Notify the user + val infoType = GroupContext.Type.UPDATE + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) + insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime) + // Send closed group update messages to any new members individually + for (member in membersToAdd) { + @Suppress("NAME_SHADOWING") + val closedGroupNewKind = ClosedGroupUpdateMessageSendJobV2.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, encryptionKeyPair, membersAsData, adminsAsData) + @Suppress("NAME_SHADOWING") + val newMemberJob = ClosedGroupUpdateMessageSendJobV2(member, closedGroupNewKind, sentTime) + ApplicationContext.getInstance(context).jobManager.add(newMemberJob) } } @JvmStatic - fun explicitNameChange(context: Context, groupPublicKey: String, newName: String): Promise { - val deferred = deferred() - ThreadUtils.queue { - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() - val members = group.members.map { it.serialize() }.toSet() - val admins = group.admins.map { it.serialize() } - val sentTime = System.currentTimeMillis() - if (group == null) { - Log.d("Loki", "Can't leave nonexistent closed group.") - return@queue deferred.reject(Error.NoThread) - } - // Send the update to the group - val kind = ClosedGroupUpdateMessageSendJobV2.Kind.NameChange(newName) - val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, kind, sentTime) - job.setContext(context) - job.onRun() // Run the job immediately - // Notify the user - val infoType = GroupContext.Type.UPDATE - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertOutgoingInfoMessage(context, groupID, infoType, newName, members, admins, threadID, sentTime) - // Update the group - groupDB.updateTitle(groupID, newName) - deferred.resolve(Unit) + fun explicitRemoveMembers(context: Context, groupPublicKey: String, membersToRemove: List) { + val userPublicKey = TextSecurePreferences.getLocalNumber(context) + val apiDB = DatabaseFactory.getLokiAPIDatabase(context) + val groupDB = DatabaseFactory.getGroupDatabase(context) + val groupID = doubleEncodeGroupID(groupPublicKey) + val group = groupDB.getGroup(groupID).orNull() + if (group == null) { + Log.d("Loki", "Can't leave nonexistent closed group.") + throw Error.NoThread + } + val updatedMembers = group.members.map { it.serialize() }.toSet() - membersToRemove + val removeMembersAsData = membersToRemove.map { Hex.fromStringCondensed(it) } + val admins = group.admins.map { it.serialize() } + val sentTime = System.currentTimeMillis() + val encryptionKeyPair = apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) + if (encryptionKeyPair == null) { + Log.d("Loki", "Couldn't get encryption key pair for closed group.") + throw Error.NoKeyPair + } + if (membersToRemove.any { it in admins } && updatedMembers.isNotEmpty()) { + Log.d("Loki", "Can't remove admin from closed group unless the group is destroyed entirely.") + throw Error.InvalidUpdate + } + val name = group.title + // Send the update to the group + val memberUpdateKind = ClosedGroupUpdateMessageSendJobV2.Kind.RemoveMembers(removeMembersAsData) + val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, memberUpdateKind, sentTime) + job.setContext(context) + job.onRun() // Run the job immediately + // Save the new group members + groupDB.updateMembers(groupID, updatedMembers.map { Address.fromSerialized(it) }) + // Notify the user + val infoType = GroupContext.Type.UPDATE + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) + insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime) + val isCurrentUserAdmin = admins.contains(userPublicKey) + if (isCurrentUserAdmin) { + generateAndSendNewEncryptionKeyPair(context, groupPublicKey, updatedMembers) } - return deferred.promise } @JvmStatic - fun leave(context: Context, groupPublicKey: String): Promise { - val userPublicKey = TextSecurePreferences.getLocalNumber(context)!! + fun explicitNameChange(context: Context, groupPublicKey: String, newName: String) { val groupDB = DatabaseFactory.getGroupDatabase(context) val groupID = doubleEncodeGroupID(groupPublicKey) val group = groupDB.getGroup(groupID).orNull() + val members = group.members.map { it.serialize() }.toSet() + val admins = group.admins.map { it.serialize() } + val sentTime = System.currentTimeMillis() if (group == null) { Log.d("Loki", "Can't leave nonexistent closed group.") - return Promise.ofFail(Error.NoThread) + throw Error.NoThread } - val name = group.title - val oldMembers = group.members.map { it.serialize() }.toSet() - val newMembers: Set - val isCurrentUserAdmin = group.admins.map { it.toString() }.contains(userPublicKey) - if (!isCurrentUserAdmin) { - newMembers = oldMembers.minus(userPublicKey) - } else { - newMembers = setOf() // If the admin leaves the group is destroyed - } - return update(context, groupPublicKey, newMembers, name) + // Send the update to the group + val kind = ClosedGroupUpdateMessageSendJobV2.Kind.NameChange(newName) + val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, kind, sentTime) + job.setContext(context) + job.onRun() // Run the job immediately + // Notify the user + val infoType = GroupContext.Type.UPDATE + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) + insertOutgoingInfoMessage(context, groupID, infoType, newName, members, admins, threadID, sentTime) + // Update the group + groupDB.updateTitle(groupID, newName) } fun update(context: Context, groupPublicKey: String, members: Collection, name: String): Promise { From dbf77159771dd38310ddc0a6c045dfe38465fcdd Mon Sep 17 00:00:00 2001 From: Brice-W Date: Fri, 19 Feb 2021 10:05:24 +1100 Subject: [PATCH 2/9] missing translations (Fixes #445 & #446) --- .../securesms/conversation/ConversationActivity.java | 4 ++-- .../thoughtcrime/securesms/loki/activities/PathActivity.kt | 4 ++-- app/src/main/res/values-fr/strings.xml | 2 ++ app/src/main/res/values-ru/strings.xml | 2 ++ app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 10 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java index 010eef08a1..deaaee2884 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationActivity.java @@ -508,7 +508,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity DatabaseFactory.getLokiThreadDatabase(this).setDelegate(this); - inputPanel.setHint("Message"); + inputPanel.setHint(getResources().getString(R.string.ConversationActivity_message)); updateSessionRestoreBanner(); @@ -2624,7 +2624,7 @@ public class ConversationActivity extends PassphraseRequiredActionBarActivity if (recipient == null) { titleTextView.setText("Compose"); } else if (allUserDevices.contains(recipient.getAddress().toString().toLowerCase())) { - titleTextView.setText("Note to Self"); + titleTextView.setText(getResources().getString(R.string.note_to_self)); } else { boolean hasName = (recipient.getName() != null && !recipient.getName().isEmpty()); titleTextView.setText(hasName ? recipient.getName() : recipient.getAddress().toString()); diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt index 810cfbff52..0fc4c3d363 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/activities/PathActivity.kt @@ -89,8 +89,8 @@ class PathActivity : PassphraseRequiredActionBarActivity() { val isGuardSnode = (OnionRequestAPI.guardSnodes.contains(snode)) getPathRow(snode, LineView.Location.Middle, index.toLong() * 1000 + 2000, dotAnimationRepeatInterval, isGuardSnode) } - val youRow = getPathRow("You", null, LineView.Location.Top, 1000, dotAnimationRepeatInterval) - val destinationRow = getPathRow("Destination", null, LineView.Location.Bottom, path.count().toLong() * 1000 + 2000, dotAnimationRepeatInterval) + val youRow = getPathRow(resources.getString(R.string.activity_path_device_row_title), null, LineView.Location.Top, 1000, dotAnimationRepeatInterval) + val destinationRow = getPathRow(resources.getString(R.string.activity_path_destination_row_title), null, LineView.Location.Bottom, path.count().toLong() * 1000 + 2000, dotAnimationRepeatInterval) val rows = listOf( youRow ) + pathRows + listOf( destinationRow ) for (row in rows) { pathRowsContainer.addView(row) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 518ae9d7c3..d562e0c62a 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -128,6 +128,8 @@ Le destinataire n’est pas une adresse texto ou courriel valide ! Le message est vide ! Membres du groupe + Note à mon intention + Message Le destinataire est invalide ! Ajouté à l’écran d’accueil Appels non pris en charge diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 4f8ad62386..a6fcf937f6 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -133,6 +133,8 @@ Адрес получателя не является ни номером телефона, ни адресом электронной почты. Пустое сообщение! Участники группы + Заметка для себя + Соощение Неверный получатель! Добавлено на главный экран Звонки не поддерживаются diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 6737a7a7a2..0ff4e7f611 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -151,6 +151,8 @@ Recipient is not a valid SMS or email address! Message is empty! Group members + Note to self + Message Invalid recipient! Added to home screen From 8df2a8af0138f8b64cae626f97473b60827e53a1 Mon Sep 17 00:00:00 2001 From: jubb Date: Fri, 19 Feb 2021 12:04:19 +1100 Subject: [PATCH 3/9] fix: send new kp to each user individually vs target group --- .../securesms/loki/protocol/ClosedGroupsProtocolV2.kt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index fff03d6b74..4c842d389c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -383,7 +383,7 @@ object ClosedGroupsProtocolV2 { pendingKeyPair[groupPublicKey] = Optional.absent() } - private fun sendEncryptionKeyPair(context: Context, groupPublicKey: String, newKeyPair: ECKeyPair, targetMembers: Collection, force: Boolean = true) { + private fun sendEncryptionKeyPair(context: Context, target: String, newKeyPair: ECKeyPair, targetMembers: Collection, force: Boolean = true) { val proto = SignalServiceProtos.KeyPair.newBuilder() proto.publicKey = ByteString.copyFrom(newKeyPair.publicKey.serialize().removing05PrefixIfNeeded()) proto.privateKey = ByteString.copyFrom(newKeyPair.privateKey.serialize()) @@ -392,7 +392,7 @@ object ClosedGroupsProtocolV2 { val ciphertext = SessionProtocolImpl(context).encrypt(plaintext, publicKey) ClosedGroupUpdateMessageSendJobV2.KeyPairWrapper(publicKey, ciphertext) } - val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, ClosedGroupUpdateMessageSendJobV2.Kind.EncryptionKeyPair(wrappers), System.currentTimeMillis()) + val job = ClosedGroupUpdateMessageSendJobV2(target, ClosedGroupUpdateMessageSendJobV2.Kind.EncryptionKeyPair(wrappers), System.currentTimeMillis()) if (force) { job.setContext(context) job.onRun() // Run the job immediately @@ -573,7 +573,9 @@ object ClosedGroupsProtocolV2 { if (encryptionKeyPair == null) { Log.d("Loki", "Couldn't get encryption key pair for closed group.") } else { - sendEncryptionKeyPair(context, groupPublicKey, encryptionKeyPair, newMembers, false) + for (user in updateMembers) { + sendEncryptionKeyPair(context, user, encryptionKeyPair, newMembers, false) + } } } } From 9df35ede1478414ac126816307a98c8a7dd07d54 Mon Sep 17 00:00:00 2001 From: jubb Date: Fri, 19 Feb 2021 17:33:23 +1100 Subject: [PATCH 4/9] feat: add sending group's public key with the target user 1 on 1 message for enc key pair --- .../ClosedGroupUpdateMessageSendJobV2.kt | 23 ++++- .../loki/protocol/ClosedGroupsProtocolV2.kt | 98 ++----------------- 2 files changed, 25 insertions(+), 96 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt index 6b4060aa17..75b1420b0e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt @@ -27,7 +27,10 @@ import org.session.libsignal.utilities.Hex import java.util.* import java.util.concurrent.TimeUnit -class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Parameters, private val destination: String, private val kind: Kind, private val sentTime: Long) : BaseJob(parameters) { +class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Parameters, + private val destination: String, + private val kind: Kind, + private val sentTime: Long) : BaseJob(parameters) { sealed class Kind { class New(val publicKey: ByteArray, val name: String, val encryptionKeyPair: ECKeyPair, val members: Collection, val admins: Collection) : Kind() @@ -36,7 +39,7 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete class RemoveMembers(val members: Collection) : Kind() class AddMembers(val members: Collection) : Kind() class NameChange(val name: String) : Kind() - class EncryptionKeyPair(val wrappers: Collection) : Kind() // The new encryption key pair encrypted for each member individually + class EncryptionKeyPair(val wrappers: Collection, val targetUser: String?) : Kind() // The new encryption key pair encrypted for each member individually } companion object { @@ -116,6 +119,7 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete builder.putString("kind", "EncryptionKeyPair") val wrappers = kind.wrappers.joinToString(" - ") { Json.encodeToString(it) } builder.putString("wrappers", wrappers) + builder.putString("targetUser", kind.targetUser) } } return builder.build() @@ -146,7 +150,8 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete } "EncryptionKeyPair" -> { val wrappers: Collection = data.getString("wrappers").split(" - ").map { Json.decodeFromString(it) } - kind = Kind.EncryptionKeyPair(wrappers) + val targetUser = data.getString("targetUser") + kind = Kind.EncryptionKeyPair(wrappers, targetUser) } "RemoveMembers" -> { val members = data.getString("members").split(" - ").map { Hex.fromStringCondensed(it) } @@ -170,6 +175,11 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete } public override fun onRun() { + val sendDestination = if (kind is Kind.EncryptionKeyPair && kind.targetUser != null) { + kind.targetUser + } else { + destination + } val contentMessage = SignalServiceProtos.Content.newBuilder() val dataMessage = SignalServiceProtos.DataMessage.newBuilder() val closedGroupUpdate = SignalServiceProtos.ClosedGroupUpdateV2.newBuilder() @@ -193,6 +203,9 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete is Kind.EncryptionKeyPair -> { closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR closedGroupUpdate.addAllWrappers(kind.wrappers.map { it.toProto() }) + if (kind.targetUser != null) { + closedGroupUpdate.publicKey = ByteString.copyFrom(kind.targetUser.toByteArray()) + } } Kind.Leave -> { closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.MEMBER_LEFT @@ -214,8 +227,8 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete contentMessage.dataMessage = dataMessage.build() val serializedContentMessage = contentMessage.build().toByteArray() val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() - val address = SignalServiceAddress(destination) - val recipient = recipient(context, destination) + val address = SignalServiceAddress(sendDestination) + val recipient = recipient(context, sendDestination) val udAccess = UnidentifiedAccessUtil.getAccessFor(context, recipient) val ttl = when (kind) { is Kind.EncryptionKeyPair -> 4 * 24 * 60 * 60 * 1000 diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index ab7c7fb307..6738bbfcee 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -2,11 +2,9 @@ package org.thoughtcrime.securesms.loki.protocol import android.content.Context import android.util.Log -import androidx.annotation.WorkerThread import com.google.protobuf.ByteString import nl.komponents.kovenant.Promise import nl.komponents.kovenant.deferred -import nl.komponents.kovenant.task import org.session.libsignal.libsignal.ecc.Curve import org.session.libsignal.libsignal.ecc.DjbECPrivateKey import org.session.libsignal.libsignal.ecc.DjbECPublicKey @@ -241,89 +239,7 @@ object ClosedGroupsProtocolV2 { groupDB.updateTitle(groupID, newName) } - fun update(context: Context, groupPublicKey: String, members: Collection, name: String): Promise { - val deferred = deferred() - ThreadUtils.queue { - val userPublicKey = TextSecurePreferences.getLocalNumber(context)!! - val apiDB = DatabaseFactory.getLokiAPIDatabase(context) - val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) - val group = groupDB.getGroup(groupID).orNull() - if (group == null) { - Log.d("Loki", "Can't update nonexistent closed group.") - return@queue deferred.reject(Error.NoThread) - } - val sentTime = System.currentTimeMillis() - 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 encryptionKeyPair = apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) - if (encryptionKeyPair == null) { - Log.d("Loki", "Couldn't get encryption key pair for closed group.") - return@queue deferred.reject(Error.NoKeyPair) - } - val removedMembers = oldMembers.minus(members) - if (removedMembers.contains(admins.first()) && members.isNotEmpty()) { - Log.d("Loki", "Can't remove admin from closed group unless the group is destroyed entirely.") - return@queue deferred.reject(Error.InvalidUpdate) - } - val isUserLeaving = removedMembers.contains(userPublicKey) - if (isUserLeaving && members.isNotEmpty()) { - if (removedMembers.count() != 1 || newMembers.isNotEmpty()) { - Log.d("Loki", "Can't remove self and add or remove others simultaneously.") - return@queue deferred.reject(Error.InvalidUpdate) - } - } - // Send the update to the group - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJobV2.Kind.Update(name, membersAsData) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, closedGroupUpdateKind, sentTime) - job.setContext(context) - job.onRun() // Run the job immediately - if (isUserLeaving) { - // Remove the group private key and unsubscribe from PNs - apiDB.removeAllClosedGroupEncryptionKeyPairs(groupPublicKey) - apiDB.removeClosedGroupPublicKey(groupPublicKey) - // Mark the group as inactive - groupDB.setActive(groupID, false) - groupDB.removeMember(groupID, Address.fromSerialized(userPublicKey)) - // Notify the PN server - LokiPushNotificationManager.performOperation(context, ClosedGroupOperation.Unsubscribe, groupPublicKey, userPublicKey) - } else { - // Generate and distribute a new encryption key pair if needed - val wasAnyUserRemoved = removedMembers.isNotEmpty() - val isCurrentUserAdmin = admins.contains(userPublicKey) - if (wasAnyUserRemoved && isCurrentUserAdmin) { - generateAndSendNewEncryptionKeyPair(context, groupPublicKey, members.minus(newMembers)) - } - // Send closed group update messages to any new members individually - for (member in newMembers) { - @Suppress("NAME_SHADOWING") - val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJobV2.Kind.New(Hex.fromStringCondensed(groupPublicKey), name, encryptionKeyPair, membersAsData, adminsAsData) - @Suppress("NAME_SHADOWING") - val job = ClosedGroupUpdateMessageSendJobV2(member, closedGroupUpdateKind, sentTime) - ApplicationContext.getInstance(context).jobManager.add(job) - } - } - // Update the group - groupDB.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 - groupDB.updateMembers(groupID, members.map { Address.fromSerialized(it) }) - } - // Notify the user - val infoType = if (isUserLeaving) GroupContext.Type.QUIT else GroupContext.Type.UPDATE - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertOutgoingInfoMessage(context, groupID, infoType, name, members, admins, threadID, sentTime) - deferred.resolve(Unit) - } - return deferred.promise - } - - fun generateAndSendNewEncryptionKeyPair(context: Context, groupPublicKey: String, targetMembers: Collection) { + private fun generateAndSendNewEncryptionKeyPair(context: Context, groupPublicKey: String, targetMembers: Collection) { // Prepare val userPublicKey = TextSecurePreferences.getLocalNumber(context) val apiDB = DatabaseFactory.getLokiAPIDatabase(context) @@ -352,7 +268,7 @@ object ClosedGroupsProtocolV2 { pendingKeyPair[groupPublicKey] = Optional.absent() } - private fun sendEncryptionKeyPair(context: Context, target: String, newKeyPair: ECKeyPair, targetMembers: Collection, force: Boolean = true) { + private fun sendEncryptionKeyPair(context: Context, groupPublicKey: String, newKeyPair: ECKeyPair, targetMembers: Collection, targetUser: String? = null, force: Boolean = true) { val proto = SignalServiceProtos.KeyPair.newBuilder() proto.publicKey = ByteString.copyFrom(newKeyPair.publicKey.serialize().removing05PrefixIfNeeded()) proto.privateKey = ByteString.copyFrom(newKeyPair.privateKey.serialize()) @@ -361,7 +277,7 @@ object ClosedGroupsProtocolV2 { val ciphertext = SessionProtocolImpl(context).encrypt(plaintext, publicKey) ClosedGroupUpdateMessageSendJobV2.KeyPairWrapper(publicKey, ciphertext) } - val job = ClosedGroupUpdateMessageSendJobV2(target, ClosedGroupUpdateMessageSendJobV2.Kind.EncryptionKeyPair(wrappers), System.currentTimeMillis()) + val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, ClosedGroupUpdateMessageSendJobV2.Kind.EncryptionKeyPair(wrappers, targetUser), System.currentTimeMillis()) if (force) { job.setContext(context) job.onRun() // Run the job immediately @@ -515,17 +431,17 @@ object ClosedGroupsProtocolV2 { Log.d("Loki", "Ignoring closed group info message for nonexistent or inactive group.") return } + // Check common group update logic if (!isValidGroupUpdate(group, sentTimestamp, senderPublicKey)) { return } val name = group.title - // Check common group update logic val members = group.members.map { it.serialize() } val admins = group.admins.map { it.serialize() } - // Users that are part of this remove update + // Users that are part of this add update val updateMembers = closedGroupUpdate.membersList.map { it.toByteArray().toHexString() } - // newMembers to save is old members minus removed members + // newMembers to save is old members plus members included in this update val newMembers = members + updateMembers groupDB.updateMembers(groupID, newMembers.map { Address.fromSerialized(it) }) @@ -543,7 +459,7 @@ object ClosedGroupsProtocolV2 { Log.d("Loki", "Couldn't get encryption key pair for closed group.") } else { for (user in updateMembers) { - sendEncryptionKeyPair(context, user, encryptionKeyPair, newMembers, false) + sendEncryptionKeyPair(context, groupPublicKey, encryptionKeyPair, newMembers, targetUser = user, force = false) } } } From 7e86343efe7e7642f589072c283b3a6e1a7df13c Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 22 Feb 2021 10:18:24 +1100 Subject: [PATCH 5/9] fix: remove the insert outgoing for local leave after network call, use groupPublicKey if envelope.source is empty in handleEncPair --- .../protocol/ClosedGroupUpdateMessageSendJobV2.kt | 4 ++-- .../loki/protocol/ClosedGroupsProtocolV2.kt | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt index 75b1420b0e..fe63e63dd7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt @@ -204,7 +204,7 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR closedGroupUpdate.addAllWrappers(kind.wrappers.map { it.toProto() }) if (kind.targetUser != null) { - closedGroupUpdate.publicKey = ByteString.copyFrom(kind.targetUser.toByteArray()) + closedGroupUpdate.publicKey = ByteString.copyFrom(destination.toByteArray()) } } Kind.Leave -> { @@ -240,7 +240,7 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete sentTime, serializedContentMessage, false, ttl, false, true, false, false, Optional.absent()) } catch (e: Exception) { - Log.d("Loki", "Failed to send closed group update message to: $destination due to error: $e.") + Log.d("Loki", "Failed to send closed group update message to: $sendDestination due to error: $e.") } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index 6738bbfcee..e6998a97c5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -103,9 +103,6 @@ object ClosedGroupsProtocolV2 { val groupDB = DatabaseFactory.getGroupDatabase(context) val groupID = doubleEncodeGroupID(groupPublicKey) val group = groupDB.getGroup(groupID).orNull() - val updatedMembers = group.members.map { it.serialize() }.toSet() - userPublicKey - val admins = group.admins.map { it.serialize() } - val name = group.title val sentTime = System.currentTimeMillis() if (group == null) { Log.d("Loki", "Can't leave nonexistent closed group.") @@ -117,9 +114,6 @@ object ClosedGroupsProtocolV2 { job.setContext(context) job.onRun() // Run the job immediately // Notify the user - val infoType = GroupContext.Type.QUIT - val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) - insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime) // Remove the group private key and unsubscribe from PNs disableLocalGroupAndUnsubscribe(context, apiDB, groupPublicKey, groupDB, groupID, userPublicKey) deferred.resolve(Unit) @@ -622,7 +616,11 @@ object ClosedGroupsProtocolV2 { val userKeyPair = apiDB.getUserX25519KeyPair() // Unwrap the message val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = doubleEncodeGroupID(groupPublicKey) + val groupID = if (groupPublicKey.isEmpty() && !closedGroupUpdate.publicKey.isEmpty) { + doubleEncodeGroupID(closedGroupUpdate.publicKey.toStringUtf8()) + } else { + doubleEncodeGroupID(groupPublicKey) + } val group = groupDB.getGroup(groupID).orNull() if (group == null) { Log.d("Loki", "Ignoring closed group encryption key pair message for nonexistent group.") From c740963fe24c47fb89a2b8d65cf215f2fe409564 Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 22 Feb 2021 10:34:21 +1100 Subject: [PATCH 6/9] fix: use a when to make logic more readable --- .../securesms/loki/protocol/ClosedGroupsProtocolV2.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index e6998a97c5..c67719cfa7 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -616,10 +616,12 @@ object ClosedGroupsProtocolV2 { val userKeyPair = apiDB.getUserX25519KeyPair() // Unwrap the message val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = if (groupPublicKey.isEmpty() && !closedGroupUpdate.publicKey.isEmpty) { - doubleEncodeGroupID(closedGroupUpdate.publicKey.toStringUtf8()) - } else { - doubleEncodeGroupID(groupPublicKey) + val groupID = when { + groupPublicKey.isNotEmpty() -> groupPublicKey + !closedGroupUpdate.publicKey.isEmpty -> closedGroupUpdate.publicKey.toStringUtf8() + else -> "" + }.let { + doubleEncodeGroupID(it) } val group = groupDB.getGroup(groupID).orNull() if (group == null) { From 766266d54dfa97a4b418e2cba4f698eb67a85990 Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 22 Feb 2021 10:40:18 +1100 Subject: [PATCH 7/9] fix: handle group of size 1 being destroyed locally for admin --- .../securesms/loki/protocol/ClosedGroupsProtocolV2.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index c67719cfa7..ee10b3926d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -509,7 +509,8 @@ object ClosedGroupsProtocolV2 { val updatedMemberList = members - senderPublicKey val userLeft = userPublicKey == senderPublicKey - if (didAdminLeave || userLeft) { + // if the admin left, we left, or we are the only remaining member: remove the group + if (didAdminLeave || userLeft || updatedMemberList.size == 1) { disableLocalGroupAndUnsubscribe(context, apiDB, groupPublicKey, groupDB, groupID, userPublicKey) } else { val isCurrentUserAdmin = admins.contains(userPublicKey) From 7f95f0f2d6695385a4d12e40c11b1cb063a41964 Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 22 Feb 2021 11:45:52 +1100 Subject: [PATCH 8/9] fix: only one wrapper and proper encoding now --- .../loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt | 2 +- .../securesms/loki/protocol/ClosedGroupsProtocolV2.kt | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt index fe63e63dd7..99197ecf5d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupUpdateMessageSendJobV2.kt @@ -204,7 +204,7 @@ class ClosedGroupUpdateMessageSendJobV2 private constructor(parameters: Paramete closedGroupUpdate.type = SignalServiceProtos.ClosedGroupUpdateV2.Type.ENCRYPTION_KEY_PAIR closedGroupUpdate.addAllWrappers(kind.wrappers.map { it.toProto() }) if (kind.targetUser != null) { - closedGroupUpdate.publicKey = ByteString.copyFrom(destination.toByteArray()) + closedGroupUpdate.publicKey = ByteString.copyFrom(Hex.fromStringCondensed(destination)) } } Kind.Leave -> { diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index ee10b3926d..5c24ad7c9b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -103,6 +103,9 @@ object ClosedGroupsProtocolV2 { val groupDB = DatabaseFactory.getGroupDatabase(context) val groupID = doubleEncodeGroupID(groupPublicKey) val group = groupDB.getGroup(groupID).orNull() + val updatedMembers = group.members.map { it.serialize() }.toSet() - userPublicKey + val admins = group.admins.map { it.serialize() } + val name = group.title val sentTime = System.currentTimeMillis() if (group == null) { Log.d("Loki", "Can't leave nonexistent closed group.") @@ -114,6 +117,9 @@ object ClosedGroupsProtocolV2 { job.setContext(context) job.onRun() // Run the job immediately // Notify the user + val infoType = GroupContext.Type.QUIT + val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(Recipient.from(context, Address.fromSerialized(groupID), false)) + insertOutgoingInfoMessage(context, groupID, infoType, name, updatedMembers, admins, threadID, sentTime) // Remove the group private key and unsubscribe from PNs disableLocalGroupAndUnsubscribe(context, apiDB, groupPublicKey, groupDB, groupID, userPublicKey) deferred.resolve(Unit) @@ -453,7 +459,7 @@ object ClosedGroupsProtocolV2 { Log.d("Loki", "Couldn't get encryption key pair for closed group.") } else { for (user in updateMembers) { - sendEncryptionKeyPair(context, groupPublicKey, encryptionKeyPair, newMembers, targetUser = user, force = false) + sendEncryptionKeyPair(context, groupPublicKey, encryptionKeyPair, setOf(user), targetUser = user, force = false) } } } @@ -619,7 +625,7 @@ object ClosedGroupsProtocolV2 { val groupDB = DatabaseFactory.getGroupDatabase(context) val groupID = when { groupPublicKey.isNotEmpty() -> groupPublicKey - !closedGroupUpdate.publicKey.isEmpty -> closedGroupUpdate.publicKey.toStringUtf8() + !closedGroupUpdate.publicKey.isEmpty -> closedGroupUpdate.publicKey.toByteArray().toHexString() else -> "" }.let { doubleEncodeGroupID(it) From 2d93d83610fb9a6f12bb8093bd0e346479aeee0f Mon Sep 17 00:00:00 2001 From: jubb Date: Mon, 22 Feb 2021 12:05:00 +1100 Subject: [PATCH 9/9] fix: store group public key as corrected public key --- .../securesms/loki/protocol/ClosedGroupsProtocolV2.kt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt index 5c24ad7c9b..72103a3df6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/loki/protocol/ClosedGroupsProtocolV2.kt @@ -623,13 +623,12 @@ object ClosedGroupsProtocolV2 { val userKeyPair = apiDB.getUserX25519KeyPair() // Unwrap the message val groupDB = DatabaseFactory.getGroupDatabase(context) - val groupID = when { + val correctGroupPublicKey = when { groupPublicKey.isNotEmpty() -> groupPublicKey !closedGroupUpdate.publicKey.isEmpty -> closedGroupUpdate.publicKey.toByteArray().toHexString() else -> "" - }.let { - doubleEncodeGroupID(it) } + val groupID = doubleEncodeGroupID(correctGroupPublicKey) val group = groupDB.getGroup(groupID).orNull() if (group == null) { Log.d("Loki", "Ignoring closed group encryption key pair message for nonexistent group.") @@ -647,7 +646,7 @@ object ClosedGroupsProtocolV2 { val proto = SignalServiceProtos.KeyPair.parseFrom(plaintext) val keyPair = ECKeyPair(DjbECPublicKey(proto.publicKey.toByteArray().removing05PrefixIfNeeded()), DjbECPrivateKey(proto.privateKey.toByteArray())) // Store it - apiDB.addClosedGroupEncryptionKeyPair(keyPair, groupPublicKey) + apiDB.addClosedGroupEncryptionKeyPair(keyPair, correctGroupPublicKey) Log.d("Loki", "Received a new closed group encryption key pair") }