From 4b6a7c145ece8c8fe04a2cd6c3d684e9a56cdfe7 Mon Sep 17 00:00:00 2001 From: 0x330a <92654767+0x330a@users.noreply.github.com> Date: Mon, 4 Dec 2023 00:11:15 +1100 Subject: [PATCH] feat: bringing the config sync message types and amendment functions into a companion extension function to build into regular member change handling / invite logic --- .../securesms/database/Storage.kt | 59 +++++++++- .../messaging/jobs/ConfigurationSyncJob.kt | 104 +++++++++--------- 2 files changed, 110 insertions(+), 53 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 6a007e1dc..e7258197c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -31,6 +31,7 @@ import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.jobs.AttachmentUploadJob import org.session.libsession.messaging.jobs.BackgroundGroupAddJob import org.session.libsession.messaging.jobs.ConfigurationSyncJob +import org.session.libsession.messaging.jobs.ConfigurationSyncJob.Companion.messageInformation import org.session.libsession.messaging.jobs.GroupAvatarDownloadJob import org.session.libsession.messaging.jobs.InviteContactsJob import org.session.libsession.messaging.jobs.Job @@ -1618,9 +1619,12 @@ open class Storage( override fun handleMemberLeft(message: GroupUpdated, closedGroupId: SessionId) { val userGroups = configFactory.userGroups ?: return + val closedGroupHexString = closedGroupId.hexString() val closedGroup = userGroups.getClosedGroup(closedGroupId.hexString()) ?: return if (closedGroup.hasAdminKey()) { // re-key and do a new config removing the previous member + val adminKey = closedGroup.adminKey + val signCallback = SnodeAPI.signingKeyCallback(adminKey) val info = configFactory.getGroupInfoConfig(closedGroupId) ?: return val members = configFactory.getGroupMemberConfig(closedGroupId) ?: return val keys = configFactory.getGroupKeysConfig(closedGroupId, info, members, free = false) ?: return @@ -1628,8 +1632,61 @@ open class Storage( keys.rekey(info, members) - val + val toDelete = mutableListOf() + val keyMessage = keys.messageInformation(closedGroupHexString, adminKey) + val infoMessage = info.messageInformation(toDelete, closedGroupHexString, adminKey) + val membersMessage = members.messageInformation(toDelete, closedGroupHexString, adminKey) + + val delete = SnodeAPI.buildAuthenticatedDeleteBatchInfo( + closedGroupHexString, + toDelete, + signCallback + ) + + val stores = listOf(keyMessage, infoMessage, membersMessage).map(ConfigurationSyncJob.ConfigMessageInformation::batch) + + val response = SnodeAPI.getSingleTargetSnode(closedGroupHexString).bind { snode -> + SnodeAPI.getRawBatchResponse( + snode, + closedGroupHexString, + stores + delete, + sequence = true + ) + } + + try { + response.get() + // todo: error handling here + + configFactory.saveGroupConfigs(keys, info, members) + + val timestamp = SnodeAPI.nowWithOffset + val messageToSign = "MEMBER_CHANGE${GroupUpdateMemberChangeMessage.Type.REMOVED.name}$timestamp" + val signature = SodiumUtilities.sign(messageToSign.toByteArray(), adminKey) + val updatedMessage = GroupUpdated( + DataMessage.GroupUpdateMessage.newBuilder() + .setMemberChangeMessage( + GroupUpdateMemberChangeMessage.newBuilder() + .addAllMemberSessionIds(listOf(message.sender!!)) + .setType(GroupUpdateMemberChangeMessage.Type.REMOVED) + .setAdminSignature(ByteString.copyFrom(signature)) + ) + .build() + ).apply { this.sentTimestamp = timestamp } + MessageSender.send(updatedMessage, fromSerialized(closedGroupHexString)) + insertGroupInfoChange(updatedMessage, closedGroupId) + info.free() + members.free() + keys.free() + } catch (e: Exception) { + Log.e("ClosedGroup", "Failed to store new key", e) + info.free() + members.free() + keys.free() + // toaster toast here + return + } } insertGroupInfoChange(message, closedGroupId) diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt index 243d1c5ef..f8de5c3b9 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/ConfigurationSyncJob.kt @@ -102,58 +102,6 @@ data class ConfigurationSyncJob(val destination: Destination) : Job { return SyncInformation(configsRequiringPush, toDelete) } - private fun ConfigBase.messageInformation(toDelete: MutableList, - destinationPubKey: String, - signingKey: ByteArray? = null, - ed25519PubKey: String? = null): ConfigMessageInformation { - val sentTimestamp = SnodeAPI.nowWithOffset - val (push, seqNo, obsoleteHashes) = push() - toDelete.addAll(obsoleteHashes) - val message = - SnodeMessage( - destinationPubKey, - Base64.encodeBytes(push), - SnodeMessage.CONFIG_TTL, - sentTimestamp - ) - - return ConfigMessageInformation( - if (signingKey != null && ed25519PubKey == null) { - SnodeAPI.buildAuthenticatedStoreBatchInfo( - namespace(), - message, - signingKey, - ) - } else SnodeAPI.buildAuthenticatedStoreBatchInfo( - namespace(), - message, - )!!, - this, - seqNo - ) - } - - private fun GroupKeysConfig.messageInformation(destinationPubKey: String, signingKey: ByteArray): ConfigMessageInformation { - val sentTimestamp = SnodeAPI.nowWithOffset - val message = - SnodeMessage( - destinationPubKey, - Base64.encodeBytes(pendingConfig()!!), // should not be null from checking has pending - SnodeMessage.CONFIG_TTL, - sentTimestamp - ) - - return ConfigMessageInformation( - SnodeAPI.buildAuthenticatedStoreBatchInfo( - namespace(), - message, - signingKey - ), - this, - 0 - ) - } - override suspend fun execute(dispatcherName: String) { val storage = MessagingModuleConfiguration.shared.storage @@ -305,6 +253,58 @@ data class ConfigurationSyncJob(val destination: Destination) : Job { // type mappings const val CONTACT_TYPE = 1 const val GROUP_TYPE = 2 + + fun ConfigBase.messageInformation(toDelete: MutableList, + destinationPubKey: String, + signingKey: ByteArray? = null, + ed25519PubKey: String? = null): ConfigMessageInformation { + val sentTimestamp = SnodeAPI.nowWithOffset + val (push, seqNo, obsoleteHashes) = push() + toDelete.addAll(obsoleteHashes) + val message = + SnodeMessage( + destinationPubKey, + Base64.encodeBytes(push), + SnodeMessage.CONFIG_TTL, + sentTimestamp + ) + + return ConfigMessageInformation( + if (signingKey != null && ed25519PubKey == null) { + SnodeAPI.buildAuthenticatedStoreBatchInfo( + namespace(), + message, + signingKey, + ) + } else SnodeAPI.buildAuthenticatedStoreBatchInfo( + namespace(), + message, + )!!, + this, + seqNo + ) + } + + fun GroupKeysConfig.messageInformation(destinationPubKey: String, signingKey: ByteArray): ConfigMessageInformation { + val sentTimestamp = SnodeAPI.nowWithOffset + val message = + SnodeMessage( + destinationPubKey, + Base64.encodeBytes(pendingConfig()!!), // should not be null from checking has pending + SnodeMessage.CONFIG_TTL, + sentTimestamp + ) + + return ConfigMessageInformation( + SnodeAPI.buildAuthenticatedStoreBatchInfo( + namespace(), + message, + signingKey + ), + this, + 0 + ) + } } class Factory : Job.Factory {