feat: add finishing of group push for initial group creation and saving group configs to DB. re-implement key signing messages properly for the signing config which doesn't override the config base

This commit is contained in:
0x330a 2023-09-11 18:07:23 +10:00
parent 2527ac2e21
commit 6067916da9
No known key found for this signature in database
GPG Key ID: 267811D6E6A2698C
12 changed files with 174 additions and 68 deletions

View File

@ -4,6 +4,8 @@ import android.content.Context
import androidx.core.content.contentValuesOf import androidx.core.content.contentValuesOf
import androidx.core.database.getBlobOrNull import androidx.core.database.getBlobOrNull
import androidx.core.database.getLongOrNull import androidx.core.database.getLongOrNull
import androidx.sqlite.db.transaction
import org.session.libsignal.protos.SignalServiceProtos.SharedConfigMessage
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(context, helper) { class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(context, helper) {
@ -20,6 +22,10 @@ class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(co
"CREATE TABLE $TABLE_NAME ($VARIANT TEXT NOT NULL, $PUBKEY TEXT NOT NULL, $DATA BLOB, $TIMESTAMP INTEGER NOT NULL DEFAULT 0, PRIMARY KEY($VARIANT, $PUBKEY));" "CREATE TABLE $TABLE_NAME ($VARIANT TEXT NOT NULL, $PUBKEY TEXT NOT NULL, $DATA BLOB, $TIMESTAMP INTEGER NOT NULL DEFAULT 0, PRIMARY KEY($VARIANT, $PUBKEY));"
private const val VARIANT_AND_PUBKEY_WHERE = "$VARIANT = ? AND $PUBKEY = ?" private const val VARIANT_AND_PUBKEY_WHERE = "$VARIANT = ? AND $PUBKEY = ?"
val KEYS_VARIANT = SharedConfigMessage.Kind.ENCRYPTION_KEYS.name
val INFO_VARIANT = SharedConfigMessage.Kind.CLOSED_GROUP_INFO.name
val MEMBER_VARIANT = SharedConfigMessage.Kind.CLOSED_GROUP_MEMBERS.name
} }
fun storeConfig(variant: String, publicKey: String, data: ByteArray, timestamp: Long) { fun storeConfig(variant: String, publicKey: String, data: ByteArray, timestamp: Long) {
@ -33,6 +39,39 @@ class ConfigDatabase(context: Context, helper: SQLCipherOpenHelper): Database(co
db.insertOrUpdate(TABLE_NAME, contentValues, VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey)) db.insertOrUpdate(TABLE_NAME, contentValues, VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey))
} }
fun storeGroupConfigs(publicKey: String, keysConfig: ByteArray, infoConfig: ByteArray, memberConfig: ByteArray, timestamp: Long) {
val db = writableDatabase
db.transaction {
val keyContent = contentValuesOf(
VARIANT to KEYS_VARIANT,
PUBKEY to publicKey,
DATA to keysConfig,
TIMESTAMP to timestamp
)
db.insertOrUpdate(TABLE_NAME, keyContent, VARIANT_AND_PUBKEY_WHERE,
arrayOf(KEYS_VARIANT, publicKey)
)
val infoContent = contentValuesOf(
VARIANT to INFO_VARIANT,
PUBKEY to publicKey,
DATA to infoConfig,
TIMESTAMP to timestamp
)
db.insertOrUpdate(TABLE_NAME, infoContent, VARIANT_AND_PUBKEY_WHERE,
arrayOf(INFO_VARIANT, publicKey)
)
val memberContent = contentValuesOf(
VARIANT to MEMBER_VARIANT,
PUBKEY to publicKey,
DATA to memberConfig,
TIMESTAMP to timestamp
)
db.insertOrUpdate(TABLE_NAME, memberContent, VARIANT_AND_PUBKEY_WHERE,
arrayOf(MEMBER_VARIANT, publicKey)
)
}
}
fun retrieveConfigAndHashes(variant: String, publicKey: String): ByteArray? { fun retrieveConfigAndHashes(variant: String, publicKey: String): ByteArray? {
val db = readableDatabase val db = readableDatabase
val query = db.query(TABLE_NAME, arrayOf(DATA), VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey),null, null, null) val query = db.query(TABLE_NAME, arrayOf(DATA), VARIANT_AND_PUBKEY_WHERE, arrayOf(variant, publicKey),null, null, null)

View File

@ -51,7 +51,6 @@ import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.messaging.open_groups.GroupMember import org.session.libsession.messaging.open_groups.GroupMember
import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.messaging.open_groups.OpenGroupApi import org.session.libsession.messaging.open_groups.OpenGroupApi
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId import org.session.libsession.messaging.sending_receiving.attachments.AttachmentId
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage
@ -62,7 +61,9 @@ import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel
import org.session.libsession.messaging.utilities.SodiumUtilities import org.session.libsession.messaging.utilities.SodiumUtilities
import org.session.libsession.messaging.utilities.UpdateMessageData import org.session.libsession.messaging.utilities.UpdateMessageData
import org.session.libsession.snode.OnionRequestAPI import org.session.libsession.snode.OnionRequestAPI
import org.session.libsession.snode.RawResponse
import org.session.libsession.snode.SnodeAPI import org.session.libsession.snode.SnodeAPI
import org.session.libsession.snode.SnodeMessage
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.Address.Companion.fromSerialized
import org.session.libsession.utilities.GroupRecord import org.session.libsession.utilities.GroupRecord
@ -882,16 +883,18 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
DatabaseComponent.get(context).groupDatabase().create(groupId, title, members, avatar, relay, admins, formationTimestamp) DatabaseComponent.get(context).groupDatabase().create(groupId, title, members, avatar, relay, admins, formationTimestamp)
} }
override suspend fun createNewGroup(groupName: String, groupDescription: String, members: Set<SessionId>): Long? { override fun createNewGroup(groupName: String, groupDescription: String, members: Set<SessionId>): Optional<Boolean> {
val userGroups = configFactory.userGroups ?: return null val userGroups = configFactory.userGroups ?: return Optional.absent()
val ourSessionId = getUserPublicKey() ?: return null val ourSessionId = getUserPublicKey() ?: return Optional.absent()
val userKp = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null val userKp = MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return Optional.absent()
val groupCreationTimestamp = SnodeAPI.nowWithOffset
val group = userGroups.createGroup() val group = userGroups.createGroup()
val adminKey = group.adminKey val adminKey = group.adminKey
userGroups.set(group) userGroups.set(group)
val groupInfo = configFactory.getOrConstructGroupInfoConfig(group.groupSessionId) ?: return null val groupInfo = configFactory.getOrConstructGroupInfoConfig(group.groupSessionId) ?: return Optional.absent()
val groupMembers = configFactory.getOrConstructGroupMemberConfig(group.groupSessionId) ?: return null val groupMembers = configFactory.getOrConstructGroupMemberConfig(group.groupSessionId) ?: return Optional.absent()
with (groupInfo) { with (groupInfo) {
setName(groupName) setName(groupName)
@ -910,9 +913,68 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
members = groupMembers members = groupMembers
) )
val newGroupRecipient = group.groupSessionId.hexString()
val configTtl = 1 * 24 * 60 * 60 * 1000L // TODO: just testing here, 1 day so we don't fill large space on network
// Test the sending // Test the sending
val keyPush = groupKeys.pendingPush() ?: return Optional.absent()
val keysSnodeMessage = SnodeMessage(
newGroupRecipient,
Base64.encodeBytes(keyPush),
configTtl,
groupCreationTimestamp
)
val keysBatchInfo = SnodeAPI.buildAuthenticatedStoreBatchInfo(
GroupKeysConfig.storageNamespace(),
keysSnodeMessage,
adminKey
)
val (infoPush, infoSeqNo) = groupInfo.push()
val infoSnodeMessage = SnodeMessage(
newGroupRecipient,
Base64.encodeBytes(keyPush),
configTtl,
groupCreationTimestamp
)
val infoBatchInfo = SnodeAPI.buildAuthenticatedStoreBatchInfo(
groupInfo.configNamespace(),
infoSnodeMessage,
adminKey
)
val (memberPush, memberSeqNo) = groupMembers.push()
val memberSnodeMessage = SnodeMessage(
newGroupRecipient,
Base64.encodeBytes(memberPush),
configTtl,
groupCreationTimestamp
)
val memberBatchInfo = SnodeAPI.buildAuthenticatedStoreBatchInfo(
groupMembers.configNamespace(),
memberSnodeMessage,
adminKey
)
try { try {
MessageSender.sendConfig(Destination.ClosedGroup(group.groupSessionId.hexString()), groupInfo, adminKey) val snode = SnodeAPI.getSingleTargetSnode(newGroupRecipient).get()
val response = SnodeAPI.getRawBatchResponse(
snode,
newGroupRecipient,
listOf(keysBatchInfo, infoBatchInfo, memberBatchInfo),
true
).get()
@Suppress("UNCHECKED_CAST")
val responseList = (response["results"] as List<RawResponse>)
val keyResponse = responseList[0]
val infoResponse = responseList[1]
val memberResponse = responseList[2]
// TODO: check response success
configFactory.saveGroupConfigs(groupKeys, groupInfo, groupMembers) // now check poller to be all
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
Log.d("Group Config", "Saved group config for $newGroupRecipient")
return Optional.of(true)
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Group Config", e) Log.e("Group Config", e)
Log.e("Group Config", "Deleting group from our group") Log.e("Group Config", "Deleting group from our group")
@ -920,7 +982,7 @@ open class Storage(context: Context, helper: SQLCipherOpenHelper, private val co
userGroups.erase(group) userGroups.erase(group)
} }
return 0 return Optional.absent()
} }
override fun createInitialConfigGroup(groupPublicKey: String, name: String, members: Map<String, Boolean>, formationTimestamp: Long, encryptionKeyPair: ECKeyPair) { override fun createInitialConfigGroup(groupPublicKey: String, name: String, members: Map<String, Boolean>, formationTimestamp: Long, encryptionKeyPair: ECKeyPair) {

View File

@ -343,4 +343,13 @@ class ConfigFactory(
return (changeTimestampMs >= (lastUpdateTimestampMs - ConfigFactory.configChangeBufferPeriod)) return (changeTimestampMs >= (lastUpdateTimestampMs - ConfigFactory.configChangeBufferPeriod))
} }
override fun saveGroupConfigs(
groupKeys: GroupKeysConfig,
groupInfo: GroupInfoConfig,
groupMembers: GroupMembersConfig
) {
val pubKey = groupInfo.id().hexString()
val timestamp = SnodeAPI.nowWithOffset
configDatabase.storeGroupConfigs(pubKey, groupKeys.dump(), groupInfo.dump(), groupMembers.dump(), timestamp)
}
} }

View File

@ -44,6 +44,7 @@ import org.session.libsession.utilities.recipients.Recipient
import org.session.libsignal.utilities.SessionId import org.session.libsignal.utilities.SessionId
import org.thoughtcrime.securesms.conversation.start.NewConversationDelegate import org.thoughtcrime.securesms.conversation.start.NewConversationDelegate
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.EditableAvatar import org.thoughtcrime.securesms.ui.EditableAvatar
import org.thoughtcrime.securesms.ui.NavigationBar import org.thoughtcrime.securesms.ui.NavigationBar
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.PreviewTheme
@ -87,20 +88,22 @@ class CreateGroupFragment : Fragment() {
fun CreateGroupScreen(viewState: ViewState, fun CreateGroupScreen(viewState: ViewState,
createGroupState: CreateGroupState, createGroupState: CreateGroupState,
modifier: Modifier = Modifier) { modifier: Modifier = Modifier) {
CreateGroup( AppTheme {
viewState, CreateGroup(
createGroupState, viewState,
onCreate = { newGroup -> createGroupState,
// launch something to create here onCreate = { newGroup ->
viewModel.tryCreateGroup(newGroup) // launch something to create here
}, viewModel.tryCreateGroup(newGroup)
onClose = { },
delegate.onDialogClosePressed() onClose = {
}, delegate.onDialogClosePressed()
onBack = { },
delegate.onDialogBackPressed() onBack = {
} delegate.onDialogBackPressed()
) }
)
}
} }
data class ViewState( data class ViewState(
@ -221,7 +224,8 @@ fun ClosedGroupPreview(
) { ) {
PreviewTheme(themeResId) { PreviewTheme(themeResId) {
CreateGroup( CreateGroup(
CreateGroupState("Group Name", "Test Group Description", emptySet()), viewState = CreateGroupFragment.ViewState(false, null, null),
createGroupState = CreateGroupState("Group Name", "Test Group Description", emptySet()),
onCreate = {}, onCreate = {},
onClose = {}, onClose = {},
onBack = {}, onBack = {},

View File

@ -2,6 +2,13 @@
#include "group_info.h" #include "group_info.h"
#include "group_members.h" #include "group_members.h"
extern "C"
JNIEXPORT jint JNICALL
Java_network_loki_messenger_libsession_1util_GroupKeysConfig_00024Companion_storageNamespace(JNIEnv* env,
jobject thiz) {
return (jint)session::config::Namespace::GroupKeys;
}
extern "C" extern "C"
JNIEXPORT jobject JNICALL JNIEXPORT jobject JNICALL
Java_network_loki_messenger_libsession_1util_GroupKeysConfig_00024Companion_newInstance(JNIEnv *env, Java_network_loki_messenger_libsession_1util_GroupKeysConfig_00024Companion_newInstance(JNIEnv *env,

View File

@ -4,7 +4,7 @@
#include "util.h" #include "util.h"
inline session::config::groups::Members* ptrToMembers(JNIEnv* env, jobject obj) { inline session::config::groups::Members* ptrToMembers(JNIEnv* env, jobject obj) {
jclass configClass = env->FindClass("network/loki/messenger/libsession_util/GroupMemberConfig"); jclass configClass = env->FindClass("network/loki/messenger/libsession_util/GroupMembersConfig");
jfieldID pointerField = env->GetFieldID(configClass, "pointer", "J"); jfieldID pointerField = env->GetFieldID(configClass, "pointer", "J");
return (session::config::groups::Members*) env->GetLongField(obj, pointerField); return (session::config::groups::Members*) env->GetLongField(obj, pointerField);
} }

View File

@ -288,8 +288,10 @@ class GroupKeysConfig(pointer: Long): ConfigBase(pointer), Closeable {
info: GroupInfoConfig, info: GroupInfoConfig,
members: GroupMembersConfig members: GroupMembersConfig
): GroupKeysConfig ): GroupKeysConfig
external fun storageNamespace(): Int
} }
external fun groupKeys(): Stack<ByteArray> external fun groupKeys(): Stack<ByteArray>
external fun keyDump(): ByteArray
external fun loadKey(hash: String, external fun loadKey(hash: String,
data: ByteArray, data: ByteArray,
msgId: ByteArray, msgId: ByteArray,

View File

@ -33,6 +33,7 @@ import org.session.libsignal.crypto.ecc.ECKeyPair
import org.session.libsignal.messages.SignalServiceAttachmentPointer import org.session.libsignal.messages.SignalServiceAttachmentPointer
import org.session.libsignal.messages.SignalServiceGroup import org.session.libsignal.messages.SignalServiceGroup
import org.session.libsignal.utilities.SessionId import org.session.libsignal.utilities.SessionId
import org.session.libsignal.utilities.guava.Optional
import network.loki.messenger.libsession_util.util.Contact as LibSessionContact import network.loki.messenger.libsession_util.util.Contact as LibSessionContact
interface StorageProtocol { interface StorageProtocol {
@ -155,7 +156,7 @@ interface StorageProtocol {
fun setExpirationTimer(address: String, duration: Int) fun setExpirationTimer(address: String, duration: Int)
// Closed Groups // Closed Groups
suspend fun createNewGroup(groupName: String, groupDescription: String, members: Set<SessionId>): Long? fun createNewGroup(groupName: String, groupDescription: String, members: Set<SessionId>): Optional<Boolean>
fun getMembers(groupPublicKey: String): List<network.loki.messenger.libsession_util.util.GroupMember> fun getMembers(groupPublicKey: String): List<network.loki.messenger.libsession_util.util.GroupMember>
// Groups // Groups

View File

@ -69,7 +69,6 @@ data class ConfigurationSyncJob(val destination: Destination): Job {
// return a list of batch request objects // return a list of batch request objects
val snodeMessage = MessageSender.buildWrappedMessageToSnode(destination, message, true) val snodeMessage = MessageSender.buildWrappedMessageToSnode(destination, message, true)
val authenticated = SnodeAPI.buildAuthenticatedStoreBatchInfo( val authenticated = SnodeAPI.buildAuthenticatedStoreBatchInfo(
destination.destinationPublicKey(),
config.configNamespace(), config.configNamespace(),
snodeMessage snodeMessage
) ?: return@map null // this entry will be null otherwise ) ?: return@map null // this entry will be null otherwise

View File

@ -1,7 +1,5 @@
package org.session.libsession.messaging.sending_receiving package org.session.libsession.messaging.sending_receiving
import androidx.annotation.WorkerThread
import network.loki.messenger.libsession_util.ConfigBase
import nl.komponents.kovenant.Promise import nl.komponents.kovenant.Promise
import nl.komponents.kovenant.deferred import nl.komponents.kovenant.deferred
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
@ -73,25 +71,6 @@ object MessageSender {
} }
} }
// New closed groups and configs not requiring additional overhead (already handled by libsession)
@WorkerThread
fun sendConfig(destination: Destination, config: ConfigBase, signingKey: ByteArray): Result<String> {
if (destination !is Destination.ClosedGroup) return Result.failure(Error.InvalidDestination(destination))
val (bytes, _) = config.push()
val testTtl = 30 * 24 * 60 * 60 * 1000L // 30 days
// handle this error thrown case
val response = SnodeAPI.sendMessage(destination, bytes, testTtl, signingKey, config.configNamespace())
Log.d("Send Config", "Response is good")
val hash = response["hash"] as? String ?: return Result.failure(Error("No returned hash of string type"))
return Result.success(hash)
}
// One-on-One Chats & Closed Groups // One-on-One Chats & Closed Groups
@Throws(Exception::class) @Throws(Exception::class)
fun buildWrappedMessageToSnode(destination: Destination, message: Message, isSyncMessage: Boolean): SnodeMessage { fun buildWrappedMessageToSnode(destination: Destination, message: Message, isSyncMessage: Boolean): SnodeMessage {

View File

@ -19,8 +19,6 @@ import nl.komponents.kovenant.functional.bind
import nl.komponents.kovenant.functional.map import nl.komponents.kovenant.functional.map
import nl.komponents.kovenant.task import nl.komponents.kovenant.task
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.Destination
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.utilities.MessageWrapper import org.session.libsession.messaging.utilities.MessageWrapper
import org.session.libsignal.crypto.getRandomElement import org.session.libsignal.crypto.getRandomElement
import org.session.libsignal.database.LokiAPIDatabaseProtocol import org.session.libsignal.database.LokiAPIDatabaseProtocol
@ -219,7 +217,7 @@ object SnodeAPI {
} }
} }
internal fun getSingleTargetSnode(publicKey: String): Promise<Snode, Exception> { fun getSingleTargetSnode(publicKey: String): Promise<Snode, Exception> {
// SecureRandom() should be cryptographically secure // SecureRandom() should be cryptographically secure
return getSwarm(publicKey).map { it.shuffled(SecureRandom()).random() } return getSwarm(publicKey).map { it.shuffled(SecureRandom()).random() }
} }
@ -374,7 +372,7 @@ object SnodeAPI {
return invoke(Snode.Method.Retrieve, snode, parameters, publicKey) return invoke(Snode.Method.Retrieve, snode, parameters, publicKey)
} }
fun buildAuthenticatedStoreBatchInfo(publicKey: String, namespace: Int, message: SnodeMessage): SnodeBatchRequestInfo? { fun buildAuthenticatedStoreBatchInfo(namespace: Int, message: SnodeMessage, signingKey: ByteArray, ed25519PubKey: String? = null): SnodeBatchRequestInfo {
val params = mutableMapOf<String, Any>() val params = mutableMapOf<String, Any>()
// load the message data params into the sub request // load the message data params into the sub request
// currently loads: // currently loads:
@ -388,13 +386,6 @@ object SnodeAPI {
// used for sig generation since it is also the value used in timestamp parameter // used for sig generation since it is also the value used in timestamp parameter
val messageTimestamp = message.timestamp val messageTimestamp = message.timestamp
val userEd25519KeyPair = try {
MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null
} catch (e: Exception) {
return null
}
val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString
val signature = ByteArray(Sign.BYTES) val signature = ByteArray(Sign.BYTES)
val verificationData = "store$namespace$messageTimestamp".toByteArray() val verificationData = "store$namespace$messageTimestamp".toByteArray()
try { try {
@ -402,13 +393,15 @@ object SnodeAPI {
signature, signature,
verificationData, verificationData,
verificationData.size.toLong(), verificationData.size.toLong(),
userEd25519KeyPair.secretKey.asBytes signingKey
) )
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Loki", "Signing data failed with user secret key", e) Log.e("Loki", "Signing data failed with user secret key", e)
} }
// timestamp already set // timestamp already set
params["pubkey_ed25519"] = ed25519PublicKey if (ed25519PubKey != null) {
params["pubkey_ed25519"] = ed25519PubKey
}
params["signature"] = Base64.encodeBytes(signature) params["signature"] = Base64.encodeBytes(signature)
return SnodeBatchRequestInfo( return SnodeBatchRequestInfo(
Snode.Method.SendMessage.rawValue, Snode.Method.SendMessage.rawValue,
@ -417,6 +410,16 @@ object SnodeAPI {
) )
} }
fun buildAuthenticatedStoreBatchInfo(namespace: Int, message: SnodeMessage): SnodeBatchRequestInfo? {
val userEd25519KeyPair = try {
MessagingModuleConfiguration.shared.getUserED25519KeyPair() ?: return null
} catch (e: Exception) {
return null
}
val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString
return buildAuthenticatedStoreBatchInfo(namespace, message, userEd25519KeyPair.secretKey.asBytes, ed25519PublicKey)
}
/** /**
* Message hashes can be shared across multiple namespaces (for a single public key destination) * Message hashes can be shared across multiple namespaces (for a single public key destination)
* @param publicKey the destination's identity public key to delete from (05...) * @param publicKey the destination's identity public key to delete from (05...)
@ -634,11 +637,8 @@ object SnodeAPI {
} }
@WorkerThread @WorkerThread
fun sendMessage(destination: Destination, rawMessage: ByteArray, ttl: Long, signingKey: ByteArray, namespace: Int): RawResponse { fun sendAuthenticatedMessage(message: SnodeMessage, signingKey: ByteArray, namespace: Int): RawResponse {
val pubKey = when (destination) { val pubKey = message.recipient
is Destination.ClosedGroup -> destination.publicKey
else -> throw MessageSender.Error.InvalidDestination(destination)
}
return retryIfNeeded(maxRetryCount) { return retryIfNeeded(maxRetryCount) {
val timestamp = nowWithOffset val timestamp = nowWithOffset
@ -654,8 +654,7 @@ object SnodeAPI {
val parameters = mapOf( val parameters = mapOf(
"pubKey" to pubKey, "pubKey" to pubKey,
"data" to Base64.encodeBytes(rawMessage), "data" to message.data,
"ttl" to ttl.toString(),
"timestamp" to timestamp.toString(), "timestamp" to timestamp.toString(),
"sig_timestamp" to timestamp.toString(), "sig_timestamp" to timestamp.toString(),
"signature" to Base64.encodeBytes(verificationData) "signature" to Base64.encodeBytes(verificationData)

View File

@ -25,6 +25,11 @@ interface ConfigFactoryProtocol {
fun conversationInConfig(publicKey: String?, groupPublicKey: String?, openGroupId: String?, visibleOnly: Boolean): Boolean fun conversationInConfig(publicKey: String?, groupPublicKey: String?, openGroupId: String?, visibleOnly: Boolean): Boolean
fun canPerformChange(variant: String, publicKey: String, changeTimestampMs: Long): Boolean fun canPerformChange(variant: String, publicKey: String, changeTimestampMs: Long): Boolean
fun saveGroupConfigs(
groupKeys: GroupKeysConfig,
groupInfo: GroupInfoConfig,
groupMembers: GroupMembersConfig
)
} }
interface ConfigFactoryUpdateListener { interface ConfigFactoryUpdateListener {