fix: add in the encryption key send from current / pending for groupID in handleMembersAdded if admin and change pendingKeyPair implementation to keyed on groupPublicKey

This commit is contained in:
Jubb 2021-02-15 12:05:04 +11:00
parent 83d107cbce
commit 93f7d428cb

View File

@ -3,7 +3,6 @@ package org.thoughtcrime.securesms.loki.protocol
import android.content.Context import android.content.Context
import android.util.Log import android.util.Log
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import kotlinx.coroutines.delay
import nl.komponents.kovenant.Promise import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.deferred import nl.komponents.kovenant.deferred
import nl.komponents.kovenant.task import nl.komponents.kovenant.task
@ -39,13 +38,13 @@ import org.session.libsession.utilities.TextSecurePreferences
import java.io.IOException import java.io.IOException
import java.util.* import java.util.*
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.ConcurrentHashMap
import kotlin.jvm.Throws import kotlin.jvm.Throws
object ClosedGroupsProtocolV2 { object ClosedGroupsProtocolV2 {
const val groupSizeLimit = 100 const val groupSizeLimit = 100
private val pendingKeyPair = AtomicReference<ECKeyPair>(null) private val pendingKeyPair = ConcurrentHashMap<String,Optional<ECKeyPair>>()
sealed class Error(val description: String) : Exception() { sealed class Error(val description: String) : Exception() {
object NoThread : Error("Couldn't find a thread associated with the given group public key") object NoThread : Error("Couldn't find a thread associated with the given group public key")
@ -141,7 +140,9 @@ object ClosedGroupsProtocolV2 {
val admins = group.admins.map { it.serialize() } val admins = group.admins.map { it.serialize() }
val adminsAsData = admins.map { Hex.fromStringCondensed(it) } val adminsAsData = admins.map { Hex.fromStringCondensed(it) }
val sentTime = System.currentTimeMillis() val sentTime = System.currentTimeMillis()
val encryptionKeyPair = pendingKeyPair.get() ?: apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) val encryptionKeyPair = pendingKeyPair.getOrElse(groupPublicKey) {
Optional.fromNullable(apiDB.getLatestClosedGroupEncryptionKeyPair(groupPublicKey))
}.orNull()
if (encryptionKeyPair == null) { if (encryptionKeyPair == null) {
Log.d("Loki", "Couldn't get encryption key pair for closed group.") Log.d("Loki", "Couldn't get encryption key pair for closed group.")
return@task Error.NoKeyPair return@task Error.NoKeyPair
@ -190,7 +191,6 @@ object ClosedGroupsProtocolV2 {
Log.d("Loki", "Can't remove admin from closed group unless the group is destroyed entirely.") Log.d("Loki", "Can't remove admin from closed group unless the group is destroyed entirely.")
return@task Error.InvalidUpdate return@task Error.InvalidUpdate
} }
val name = group.title
// Send the update to the group // Send the update to the group
val memberUpdateKind = ClosedGroupUpdateMessageSendJobV2.Kind.RemoveMembers(removeMembersAsData) val memberUpdateKind = ClosedGroupUpdateMessageSendJobV2.Kind.RemoveMembers(removeMembersAsData)
val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, memberUpdateKind, sentTime) val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, memberUpdateKind, sentTime)
@ -351,11 +351,19 @@ object ClosedGroupsProtocolV2 {
} }
// Generate the new encryption key pair // Generate the new encryption key pair
val newKeyPair = Curve.generateKeyPair() val newKeyPair = Curve.generateKeyPair()
// replace call will not succeed if no value already set
pendingKeyPair.putIfAbsent(groupPublicKey,Optional.absent())
do { do {
// make sure we set the pendingKeyPair or wait until it is not null // make sure we set the pendingKeyPair or wait until it is not null
} while (!pendingKeyPair.compareAndSet(null, newKeyPair)) } while (!pendingKeyPair.replace(groupPublicKey,Optional.absent(),Optional.fromNullable(newKeyPair)))
// Distribute it // Distribute it
sendEncryptionKeyPair(context, groupPublicKey, newKeyPair, targetMembers)
// Store it * after * having sent out the message to the group
apiDB.addClosedGroupEncryptionKeyPair(newKeyPair, groupPublicKey)
pendingKeyPair[groupPublicKey] = Optional.absent()
}
private fun sendEncryptionKeyPair(context: Context, groupPublicKey: String, newKeyPair: ECKeyPair, targetMembers: Collection<String>, force: Boolean = true) {
val proto = SignalServiceProtos.KeyPair.newBuilder() val proto = SignalServiceProtos.KeyPair.newBuilder()
proto.publicKey = ByteString.copyFrom(newKeyPair.publicKey.serialize().removing05PrefixIfNeeded()) proto.publicKey = ByteString.copyFrom(newKeyPair.publicKey.serialize().removing05PrefixIfNeeded())
proto.privateKey = ByteString.copyFrom(newKeyPair.privateKey.serialize()) proto.privateKey = ByteString.copyFrom(newKeyPair.privateKey.serialize())
@ -365,11 +373,12 @@ object ClosedGroupsProtocolV2 {
ClosedGroupUpdateMessageSendJobV2.KeyPairWrapper(publicKey, ciphertext) ClosedGroupUpdateMessageSendJobV2.KeyPairWrapper(publicKey, ciphertext)
} }
val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, ClosedGroupUpdateMessageSendJobV2.Kind.EncryptionKeyPair(wrappers), System.currentTimeMillis()) val job = ClosedGroupUpdateMessageSendJobV2(groupPublicKey, ClosedGroupUpdateMessageSendJobV2.Kind.EncryptionKeyPair(wrappers), System.currentTimeMillis())
if (force) {
job.setContext(context) job.setContext(context)
job.onRun() // Run the job immediately job.onRun() // Run the job immediately
// Store it * after * having sent out the message to the group } else {
apiDB.addClosedGroupEncryptionKeyPair(newKeyPair, groupPublicKey) ApplicationContext.getInstance(context).jobManager.add(job)
pendingKeyPair.set(null) }
} }
@JvmStatic @JvmStatic
@ -509,6 +518,7 @@ object ClosedGroupsProtocolV2 {
fun handleClosedGroupMembersAdded(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) { fun handleClosedGroupMembersAdded(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {
val userPublicKey = TextSecurePreferences.getLocalNumber(context) val userPublicKey = TextSecurePreferences.getLocalNumber(context)
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
val groupDB = DatabaseFactory.getGroupDatabase(context) val groupDB = DatabaseFactory.getGroupDatabase(context)
val groupID = doubleEncodeGroupID(groupPublicKey) val groupID = doubleEncodeGroupID(groupPublicKey)
val group = groupDB.getGroup(groupID).orNull() val group = groupDB.getGroup(groupID).orNull()
@ -536,6 +546,17 @@ object ClosedGroupsProtocolV2 {
} else { } else {
insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins) insertIncomingInfoMessage(context, senderPublicKey, groupID, GroupContext.Type.UPDATE, SignalServiceGroup.Type.UPDATE, name, members, admins)
} }
if (userPublicKey in admins) {
// send current encryption key to the latest added members
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.")
} else {
sendEncryptionKeyPair(context, groupPublicKey, encryptionKeyPair, newMembers, false)
}
}
} }
fun handleClosedGroupNameChange(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) { fun handleClosedGroupNameChange(context: Context, closedGroupUpdate: SignalServiceProtos.ClosedGroupUpdateV2, sentTimestamp: Long, groupPublicKey: String, senderPublicKey: String) {