handle expiration timer with NEW group update type

This commit is contained in:
Brice-W 2021-06-09 14:31:05 +10:00
parent 534e0e8e69
commit 3b03aef80f
5 changed files with 26 additions and 9 deletions

View File

@ -428,6 +428,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
DatabaseFactory.getLokiAPIDatabase(context).removeAllClosedGroupEncryptionKeyPairs(groupPublicKey) DatabaseFactory.getLokiAPIDatabase(context).removeAllClosedGroupEncryptionKeyPairs(groupPublicKey)
} }
override fun setExpirationTimer(groupID: String, duration: Int) {
val recipient = Recipient.from(context, fromSerialized(groupID), false)
DatabaseFactory.getRecipientDatabase(context).setExpireMessages(recipient, duration);
}
override fun getAllV2OpenGroups(): Map<Long, OpenGroupV2> { override fun getAllV2OpenGroups(): Map<Long, OpenGroupV2> {
return DatabaseFactory.getLokiThreadDatabase(context).getAllV2OpenGroups() return DatabaseFactory.getLokiThreadDatabase(context).getAllV2OpenGroups()
} }

View File

@ -115,6 +115,7 @@ interface StorageProtocol {
fun isClosedGroup(publicKey: String): Boolean fun isClosedGroup(publicKey: String): Boolean
fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): MutableList<ECKeyPair> fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): MutableList<ECKeyPair>
fun getLatestClosedGroupEncryptionKeyPair(groupPublicKey: String): ECKeyPair? fun getLatestClosedGroupEncryptionKeyPair(groupPublicKey: String): ECKeyPair?
fun setExpirationTimer(groupID: String, duration: Int)
// Groups // Groups
fun getAllGroups(): List<GroupRecord> fun getAllGroups(): List<GroupRecord>

View File

@ -34,6 +34,7 @@ class ClosedGroupControlMessage() : ControlMessage() {
is Kind.New -> { is Kind.New -> {
!kind.publicKey.isEmpty && kind.name.isNotEmpty() && kind.encryptionKeyPair?.publicKey != null !kind.publicKey.isEmpty && kind.name.isNotEmpty() && kind.encryptionKeyPair?.publicKey != null
&& kind.encryptionKeyPair?.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty() && kind.encryptionKeyPair?.privateKey != null && kind.members.isNotEmpty() && kind.admins.isNotEmpty()
&& kind.expireTimer >= 0
} }
is Kind.EncryptionKeyPair -> true is Kind.EncryptionKeyPair -> true
is Kind.NameChange -> kind.name.isNotEmpty() is Kind.NameChange -> kind.name.isNotEmpty()
@ -44,8 +45,8 @@ class ClosedGroupControlMessage() : ControlMessage() {
} }
sealed class Kind { sealed class Kind {
class New(var publicKey: ByteString, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<ByteString>, var admins: List<ByteString>) : Kind() { class New(var publicKey: ByteString, var name: String, var encryptionKeyPair: ECKeyPair?, var members: List<ByteString>, var admins: List<ByteString>, var expireTimer: Int) : Kind() {
internal constructor() : this(ByteString.EMPTY, "", null, listOf(), listOf()) internal constructor() : this(ByteString.EMPTY, "", null, listOf(), listOf(), 0)
} }
/** An encryption key pair encrypted for each member individually. /** An encryption key pair encrypted for each member individually.
* *
@ -88,10 +89,11 @@ class ClosedGroupControlMessage() : ControlMessage() {
val publicKey = closedGroupControlMessageProto.publicKey ?: return null val publicKey = closedGroupControlMessageProto.publicKey ?: return null
val name = closedGroupControlMessageProto.name ?: return null val name = closedGroupControlMessageProto.name ?: return null
val encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair ?: return null val encryptionKeyPairAsProto = closedGroupControlMessageProto.encryptionKeyPair ?: return null
val expireTimer = closedGroupControlMessageProto.expireTimer
try { try {
val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray()), val encryptionKeyPair = ECKeyPair(DjbECPublicKey(encryptionKeyPairAsProto.publicKey.toByteArray()),
DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray())) DjbECPrivateKey(encryptionKeyPairAsProto.privateKey.toByteArray()))
kind = Kind.New(publicKey, name, encryptionKeyPair, closedGroupControlMessageProto.membersList, closedGroupControlMessageProto.adminsList) kind = Kind.New(publicKey, name, encryptionKeyPair, closedGroupControlMessageProto.membersList, closedGroupControlMessageProto.adminsList, expireTimer)
} catch (e: Exception) { } catch (e: Exception) {
Log.w(TAG, "Couldn't parse key pair from proto: $encryptionKeyPairAsProto.") Log.w(TAG, "Couldn't parse key pair from proto: $encryptionKeyPairAsProto.")
return null return null
@ -140,9 +142,10 @@ class ClosedGroupControlMessage() : ControlMessage() {
val encryptionKeyPair = SignalServiceProtos.KeyPair.newBuilder() val encryptionKeyPair = SignalServiceProtos.KeyPair.newBuilder()
encryptionKeyPair.publicKey = ByteString.copyFrom(kind.encryptionKeyPair!!.publicKey.serialize().removing05PrefixIfNeeded()) encryptionKeyPair.publicKey = ByteString.copyFrom(kind.encryptionKeyPair!!.publicKey.serialize().removing05PrefixIfNeeded())
encryptionKeyPair.privateKey = ByteString.copyFrom(kind.encryptionKeyPair!!.privateKey.serialize()) encryptionKeyPair.privateKey = ByteString.copyFrom(kind.encryptionKeyPair!!.privateKey.serialize())
closedGroupControlMessage.encryptionKeyPair = encryptionKeyPair.build() closedGroupControlMessage.encryptionKeyPair = encryptionKeyPair.build()
closedGroupControlMessage.addAllMembers(kind.members) closedGroupControlMessage.addAllMembers(kind.members)
closedGroupControlMessage.addAllAdmins(kind.admins) closedGroupControlMessage.addAllAdmins(kind.admins)
closedGroupControlMessage.expireTimer = kind.expireTimer
} }
is Kind.EncryptionKeyPair -> { is Kind.EncryptionKeyPair -> {
closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR closedGroupControlMessage.type = DataMessage.ClosedGroupControlMessage.Type.ENCRYPTION_KEY_PAIR

View File

@ -11,8 +11,10 @@ import org.session.libsession.messaging.sending_receiving.MessageSender.Error
import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI import org.session.libsession.messaging.sending_receiving.notifications.PushNotificationAPI
import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2 import org.session.libsession.messaging.sending_receiving.pollers.ClosedGroupPollerV2
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
import org.session.libsession.utilities.Address.Companion.fromSerialized
import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.GroupUtil
import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.crypto.ecc.Curve import org.session.libsignal.crypto.ecc.Curve
import org.session.libsignal.crypto.ecc.ECKeyPair import org.session.libsignal.crypto.ecc.ECKeyPair
import org.session.libsignal.utilities.guava.Optional import org.session.libsignal.utilities.guava.Optional
@ -50,7 +52,7 @@ fun MessageSender.create(name: String, members: Collection<String>): Promise<Str
null, null, LinkedList(admins.map { Address.fromSerialized(it) }), System.currentTimeMillis()) null, null, LinkedList(admins.map { Address.fromSerialized(it) }), System.currentTimeMillis())
storage.setProfileSharing(Address.fromSerialized(groupID), true) storage.setProfileSharing(Address.fromSerialized(groupID), true)
// Send a closed group update message to all members individually // Send a closed group update message to all members individually
val closedGroupUpdateKind = ClosedGroupControlMessage.Kind.New(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), name, encryptionKeyPair, membersAsData, adminsAsData) val closedGroupUpdateKind = ClosedGroupControlMessage.Kind.New(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), name, encryptionKeyPair, membersAsData, adminsAsData, 0)
val sentTime = System.currentTimeMillis() val sentTime = System.currentTimeMillis()
for (member in members) { for (member in members) {
val closedGroupControlMessage = ClosedGroupControlMessage(closedGroupUpdateKind) val closedGroupControlMessage = ClosedGroupControlMessage(closedGroupUpdateKind)
@ -131,6 +133,9 @@ fun MessageSender.addMembers(groupPublicKey: String, membersToAdd: List<String>)
Log.d("Loki", "Can't add members to nonexistent closed group.") Log.d("Loki", "Can't add members to nonexistent closed group.")
throw Error.NoThread throw Error.NoThread
} }
// Get expiration timer value for the group
val recipient = Recipient.from(context, fromSerialized(groupID), false)
val expireTimer = recipient.expireMessages
if (membersToAdd.isEmpty()) { if (membersToAdd.isEmpty()) {
Log.d("Loki", "Invalid closed group update.") Log.d("Loki", "Invalid closed group update.")
throw Error.InvalidClosedGroupUpdate throw Error.InvalidClosedGroupUpdate
@ -155,7 +160,7 @@ fun MessageSender.addMembers(groupPublicKey: String, membersToAdd: List<String>)
send(closedGroupControlMessage, Address.fromSerialized(groupID)) send(closedGroupControlMessage, Address.fromSerialized(groupID))
// Send closed group update messages to any new members individually // Send closed group update messages to any new members individually
for (member in membersToAdd) { for (member in membersToAdd) {
val closedGroupNewKind = ClosedGroupControlMessage.Kind.New(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), name, encryptionKeyPair, membersAsData, adminsAsData) val closedGroupNewKind = ClosedGroupControlMessage.Kind.New(ByteString.copyFrom(Hex.fromStringCondensed(groupPublicKey)), name, encryptionKeyPair, membersAsData, adminsAsData, expireTimer)
val closedGroupControlMessage = ClosedGroupControlMessage(closedGroupNewKind) val closedGroupControlMessage = ClosedGroupControlMessage(closedGroupNewKind)
closedGroupControlMessage.sentTimestamp = sentTime closedGroupControlMessage.sentTimestamp = sentTime
send(closedGroupControlMessage, Address.fromSerialized(member)) send(closedGroupControlMessage, Address.fromSerialized(member))

View File

@ -121,7 +121,7 @@ private fun handleConfigurationMessage(message: ConfigurationMessage) {
for (closedGroup in message.closedGroups) { for (closedGroup in message.closedGroups) {
if (allClosedGroupPublicKeys.contains(closedGroup.publicKey)) continue if (allClosedGroupPublicKeys.contains(closedGroup.publicKey)) continue
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, closedGroup.publicKey, closedGroup.name, handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, closedGroup.publicKey, closedGroup.name,
closedGroup.encryptionKeyPair!!, closedGroup.members, closedGroup.admins, message.sentTimestamp!!) closedGroup.encryptionKeyPair!!, closedGroup.members, closedGroup.admins, message.sentTimestamp!!, 0)
} }
val allV2OpenGroups = storage.getAllV2OpenGroups().map { it.value.joinURL } val allV2OpenGroups = storage.getAllV2OpenGroups().map { it.value.joinURL }
for (openGroup in message.openGroups) { for (openGroup in message.openGroups) {
@ -256,10 +256,11 @@ private fun MessageReceiver.handleNewClosedGroup(message: ClosedGroupControlMess
val groupPublicKey = kind.publicKey.toByteArray().toHexString() val groupPublicKey = kind.publicKey.toByteArray().toHexString()
val members = kind.members.map { it.toByteArray().toHexString() } val members = kind.members.map { it.toByteArray().toHexString() }
val admins = kind.admins.map { it.toByteArray().toHexString() } val admins = kind.admins.map { it.toByteArray().toHexString() }
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!) val expireTimer = kind.expireTimer
handleNewClosedGroup(message.sender!!, message.sentTimestamp!!, groupPublicKey, kind.name, kind.encryptionKeyPair!!, members, admins, message.sentTimestamp!!, expireTimer)
} }
private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: List<String>, admins: List<String>, formationTimestamp: Long) { private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPublicKey: String, name: String, encryptionKeyPair: ECKeyPair, members: List<String>, admins: List<String>, formationTimestamp: Long, expireTimer: Int) {
val context = MessagingModuleConfiguration.shared.context val context = MessagingModuleConfiguration.shared.context
val storage = MessagingModuleConfiguration.shared.storage val storage = MessagingModuleConfiguration.shared.storage
// Create the group // Create the group
@ -289,6 +290,8 @@ private fun handleNewClosedGroup(sender: String, sentTimestamp: Long, groupPubli
storage.addClosedGroupPublicKey(groupPublicKey) storage.addClosedGroupPublicKey(groupPublicKey)
// Store the encryption key pair // Store the encryption key pair
storage.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey) storage.addClosedGroupEncryptionKeyPair(encryptionKeyPair, groupPublicKey)
// Set expiration timer
storage.setExpirationTimer(groupID, expireTimer)
// Notify the PN server // Notify the PN server
PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, storage.getUserPublicKey()!!) PushNotificationAPI.performOperation(PushNotificationAPI.ClosedGroupOperation.Subscribe, groupPublicKey, storage.getUserPublicKey()!!)
// Start polling // Start polling