feat(WIP): build an authenticated delete with sign callback
This commit is contained in:
parent
2fc83b87ad
commit
b62cca2612
|
@ -1,5 +1,6 @@
|
||||||
package org.thoughtcrime.securesms.dependencies
|
package org.thoughtcrime.securesms.dependencies
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.SupervisorJob
|
import kotlinx.coroutines.SupervisorJob
|
||||||
import kotlinx.coroutines.plus
|
import kotlinx.coroutines.plus
|
||||||
|
@ -8,6 +9,7 @@ import org.session.libsignal.utilities.SessionId
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
|
||||||
class PollerFactory(private val scope: CoroutineScope,
|
class PollerFactory(private val scope: CoroutineScope,
|
||||||
|
private val executor: CoroutineDispatcher,
|
||||||
private val configFactory: ConfigFactory) {
|
private val configFactory: ConfigFactory) {
|
||||||
|
|
||||||
private val pollers = ConcurrentHashMap<SessionId, ClosedGroupPoller>()
|
private val pollers = ConcurrentHashMap<SessionId, ClosedGroupPoller>()
|
||||||
|
@ -17,7 +19,7 @@ class PollerFactory(private val scope: CoroutineScope,
|
||||||
configFactory.userGroups?.getClosedGroup(sessionId.hexString()) ?: return null
|
configFactory.userGroups?.getClosedGroup(sessionId.hexString()) ?: return null
|
||||||
|
|
||||||
return pollers.getOrPut(sessionId) {
|
return pollers.getOrPut(sessionId) {
|
||||||
ClosedGroupPoller(scope + SupervisorJob(), sessionId, configFactory)
|
ClosedGroupPoller(scope + SupervisorJob(), executor, sessionId, configFactory)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,10 @@ import dagger.Provides
|
||||||
import dagger.hilt.InstallIn
|
import dagger.hilt.InstallIn
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import org.session.libsession.utilities.ConfigFactoryUpdateListener
|
import org.session.libsession.utilities.ConfigFactoryUpdateListener
|
||||||
import org.session.libsession.utilities.TextSecurePreferences
|
import org.session.libsession.utilities.TextSecurePreferences
|
||||||
|
@ -42,9 +45,15 @@ object SessionUtilModule {
|
||||||
@Named(POLLER_SCOPE)
|
@Named(POLLER_SCOPE)
|
||||||
fun providePollerScope(@ApplicationContext applicationContext: Context): CoroutineScope = GlobalScope
|
fun providePollerScope(@ApplicationContext applicationContext: Context): CoroutineScope = GlobalScope
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@Provides
|
||||||
|
@Named(POLLER_SCOPE)
|
||||||
|
fun provideExecutor(): CoroutineDispatcher = Dispatchers.IO.limitedParallelism(1)
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun providePollerFactory(@Named(POLLER_SCOPE) coroutineScope: CoroutineScope,
|
fun providePollerFactory(@Named(POLLER_SCOPE) coroutineScope: CoroutineScope,
|
||||||
configFactory: ConfigFactory) = PollerFactory(coroutineScope, configFactory)
|
@Named(POLLER_SCOPE) dispatcher: CoroutineDispatcher,
|
||||||
|
configFactory: ConfigFactory) = PollerFactory(coroutineScope, dispatcher, configFactory)
|
||||||
|
|
||||||
}
|
}
|
|
@ -84,7 +84,7 @@ data class ConfigurationSyncJob(val destination: Destination) : Job {
|
||||||
if (config is GroupKeysConfig) {
|
if (config is GroupKeysConfig) {
|
||||||
config.messageInformation(destination.publicKey, signingKey)
|
config.messageInformation(destination.publicKey, signingKey)
|
||||||
} else if (config is ConfigBase) {
|
} else if (config is ConfigBase) {
|
||||||
config.messageInformation(toDelete, destination.publicKey, signingKey, groupId.publicKey)
|
config.messageInformation(toDelete, destination.publicKey, signingKey)
|
||||||
} else {
|
} else {
|
||||||
Log.e("ConfigurationSyncJob", "Tried to create a message from an unknown config")
|
Log.e("ConfigurationSyncJob", "Tried to create a message from an unknown config")
|
||||||
null
|
null
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package org.session.libsession.messaging.sending_receiving.pollers
|
package org.session.libsession.messaging.sending_receiving.pollers
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.isActive
|
import kotlinx.coroutines.isActive
|
||||||
|
@ -26,7 +26,8 @@ import org.session.libsignal.utilities.SessionId
|
||||||
import org.session.libsignal.utilities.Snode
|
import org.session.libsignal.utilities.Snode
|
||||||
import kotlin.time.Duration.Companion.days
|
import kotlin.time.Duration.Companion.days
|
||||||
|
|
||||||
class ClosedGroupPoller(private val executor: CoroutineScope,
|
class ClosedGroupPoller(private val scope: CoroutineScope,
|
||||||
|
private val execute: CoroutineDispatcher,
|
||||||
private val closedGroupSessionId: SessionId,
|
private val closedGroupSessionId: SessionId,
|
||||||
private val configFactoryProtocol: ConfigFactoryProtocol) {
|
private val configFactoryProtocol: ConfigFactoryProtocol) {
|
||||||
|
|
||||||
|
@ -69,7 +70,7 @@ class ClosedGroupPoller(private val executor: CoroutineScope,
|
||||||
|
|
||||||
if (ENABLE_LOGGING) Log.d("ClosedGroupPoller", "Starting closed group poller for ${closedGroupSessionId.hexString().take(4)}")
|
if (ENABLE_LOGGING) Log.d("ClosedGroupPoller", "Starting closed group poller for ${closedGroupSessionId.hexString().take(4)}")
|
||||||
job?.cancel()
|
job?.cancel()
|
||||||
job = executor.launch(Dispatchers.IO) {
|
job = scope.launch {
|
||||||
val closedGroups = configFactoryProtocol.userGroups ?: return@launch
|
val closedGroups = configFactoryProtocol.userGroups ?: return@launch
|
||||||
isRunning = true
|
isRunning = true
|
||||||
while (isActive && isRunning) {
|
while (isActive && isRunning) {
|
||||||
|
@ -105,6 +106,8 @@ class ClosedGroupPoller(private val executor: CoroutineScope,
|
||||||
free = false
|
free = false
|
||||||
) ?: return null
|
) ?: return null
|
||||||
|
|
||||||
|
val isAdmin = group.hasAdminKey()
|
||||||
|
|
||||||
val hashesToExtend = mutableSetOf<String>()
|
val hashesToExtend = mutableSetOf<String>()
|
||||||
|
|
||||||
hashesToExtend += info.currentHashes()
|
hashesToExtend += info.currentHashes()
|
||||||
|
@ -152,7 +155,7 @@ class ClosedGroupPoller(private val executor: CoroutineScope,
|
||||||
|
|
||||||
val requests = mutableListOf(keysPoll, infoPoll, membersPoll, messagePoll)
|
val requests = mutableListOf(keysPoll, infoPoll, membersPoll, messagePoll)
|
||||||
|
|
||||||
if (hashesToExtend.isNotEmpty()) {
|
if (isAdmin && hashesToExtend.isNotEmpty()) {
|
||||||
SnodeAPI.buildAuthenticatedAlterTtlBatchRequest(
|
SnodeAPI.buildAuthenticatedAlterTtlBatchRequest(
|
||||||
messageHashes = hashesToExtend.toList(),
|
messageHashes = hashesToExtend.toList(),
|
||||||
publicKey = closedGroupSessionId.hexString(),
|
publicKey = closedGroupSessionId.hexString(),
|
||||||
|
|
|
@ -439,21 +439,7 @@ object SnodeAPI {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString
|
val ed25519PublicKey = userEd25519KeyPair.publicKey.asHexString
|
||||||
val signature = ByteArray(Sign.BYTES)
|
val signCallback = signingKeyCallback(userEd25519KeyPair.secretKey.asBytes)
|
||||||
val verificationData = "delete${messageHashes.joinToString("")}".toByteArray()
|
|
||||||
try {
|
|
||||||
sodium.cryptoSignDetached(
|
|
||||||
signature,
|
|
||||||
verificationData,
|
|
||||||
verificationData.size.toLong(),
|
|
||||||
userEd25519KeyPair.secretKey.asBytes
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Loki", "Signing data failed with user secret key", e)
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
params["pubkey_ed25519"] = ed25519PublicKey
|
|
||||||
params["signature"] = Base64.encodeBytes(signature)
|
|
||||||
return SnodeBatchRequestInfo(
|
return SnodeBatchRequestInfo(
|
||||||
Snode.Method.DeleteMessage.rawValue,
|
Snode.Method.DeleteMessage.rawValue,
|
||||||
params,
|
params,
|
||||||
|
@ -461,6 +447,14 @@ object SnodeAPI {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun buildAuthenticatedDeleteBatchInfo(
|
||||||
|
publicKey: String,
|
||||||
|
messageHashes: List<String>,
|
||||||
|
signCallback: SignCallback,
|
||||||
|
required: Boolean = false): SnodeBatchRequestInfo? {
|
||||||
|
val verificationData = "delete${messageHashes.joinToString("")}".toByteArray()
|
||||||
|
}
|
||||||
|
|
||||||
fun buildAuthenticatedRetrieveBatchRequest(snode: Snode,
|
fun buildAuthenticatedRetrieveBatchRequest(snode: Snode,
|
||||||
publicKey: String,
|
publicKey: String,
|
||||||
namespace: Int,
|
namespace: Int,
|
||||||
|
|
Loading…
Reference in New Issue