WIP: clean up V1 multi device

This commit is contained in:
Ryan ZHAO 2021-02-17 10:51:02 +11:00
parent c5cc191ff3
commit 3285975b1a
19 changed files with 19 additions and 791 deletions

View File

@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.loki.activities
import android.content.Context
import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.loki.utilities.ContactUtilities
import org.thoughtcrime.securesms.util.AsyncLoader
class EditClosedGroupLoader(context: Context, val groupID: String) : AsyncLoader<List<String>>(context) {

View File

@ -44,12 +44,9 @@ import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.TextSecurePreferences.getBooleanPreference
import org.session.libsession.utilities.TextSecurePreferences.setBooleanPreference
import org.session.libsession.utilities.Util
import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
import org.session.libsignal.service.loki.protocol.mentions.MentionsManager
import org.session.libsignal.service.loki.protocol.meta.SessionMetaProtocol
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol
import org.session.libsignal.utilities.ThreadUtils
import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.loki.dialogs.*
@ -177,20 +174,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity, ConversationClickListe
if (userPublicKey != null) {
MentionsManager.configureIfNeeded(userPublicKey, threadDB, userDB)
SessionMetaProtocol.configureIfNeeded(apiDB, userPublicKey)
SyncMessagesProtocol.configureIfNeeded(apiDB, userPublicKey)
application.publicChatManager.startPollersIfNeeded()
}
SessionManagementProtocol.configureIfNeeded(sessionResetImpl, sskDatabase, application)
MultiDeviceProtocol.configureIfNeeded(apiDB)
IP2Country.configureIfNeeded(this)
application.registerForFCMIfNeeded(false)
// Preload device links to make message sending quicker
val publicKeys = ContactUtilities.getAllContacts(this).filter { contact ->
!contact.recipient.isGroupRecipient && !contact.isOurDevice && !contact.isSlave
}.map {
it.recipient.address.toString()
}.toSet()
FileServerAPI.shared.getDeviceLinks(publicKeys)
// Observe blocked contacts changed events
val broadcastReceiver = object : BroadcastReceiver() {

View File

@ -27,7 +27,6 @@ import org.session.libsignal.service.loki.api.fileserver.FileServerAPI
import org.session.libsignal.service.loki.api.opengroups.PublicChat
import org.session.libsignal.service.loki.api.opengroups.PublicChatAPI
import org.session.libsignal.service.loki.api.opengroups.PublicChatMessage
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
import java.security.MessageDigest
import java.util.*
@ -161,14 +160,11 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
fun pollForNewMessages(): Promise<Unit, Exception> {
fun processIncomingMessage(message: PublicChatMessage) {
// If the sender of the current message is not a slave device, set the display name in the database
val masterHexEncodedPublicKey = MultiDeviceProtocol.shared.getMasterDevice(message.senderPublicKey)
if (masterHexEncodedPublicKey == null) {
val senderDisplayName = "${message.displayName} (...${message.senderPublicKey.takeLast(8)})"
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.senderPublicKey, senderDisplayName)
}
val senderHexEncodedPublicKey = masterHexEncodedPublicKey ?: message.senderPublicKey
val senderDisplayName = "${message.displayName} (...${message.senderPublicKey.takeLast(8)})"
DatabaseFactory.getLokiUserDatabase(context).setServerDisplayName(group.id, message.senderPublicKey, senderDisplayName)
val senderHexEncodedPublicKey = message.senderPublicKey
val serviceDataMessage = getDataMessage(message)
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.serverTimestamp, false, false)
val serviceContent = SignalServiceContent(serviceDataMessage, senderHexEncodedPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.serverTimestamp, false)
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) {
PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
} else {
@ -221,8 +217,6 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
}
if (isPollOngoing) { return Promise.of(Unit) }
isPollOngoing = true
val userDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userHexEncodedPublicKey)
var uniqueDevices = setOf<String>()
val userPrivateKey = IdentityKeyUtil.getIdentityKeyPair(context).privateKey.serialize()
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
FileServerAPI.configure(userHexEncodedPublicKey, userPrivateKey, apiDB)
@ -240,20 +234,10 @@ class PublicChatPoller(private val context: Context, private val group: PublicCh
*/
Promise.of(messages)
}
promise.successBackground {
/*
val newDisplayNameUpdatees = uniqueDevices.mapNotNull {
// This will return null if the current device is a master device
MultiDeviceProtocol.shared.getMasterDevice(it)
}.toSet()
// Fetch the display names of the master devices
displayNameUpdatees = displayNameUpdatees.union(newDisplayNameUpdatees)
*/
}
promise.successBackground { messages ->
// Process messages in the background
messages.forEach { message ->
if (userDevices.contains(message.senderPublicKey)) {
if (message.senderPublicKey == userHexEncodedPublicKey) {
processOutgoingMessage(message)
} else {
processIncomingMessage(message)

View File

@ -11,7 +11,6 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
import org.thoughtcrime.securesms.loki.utilities.*
import org.session.libsignal.service.loki.api.Snode
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
import org.session.libsignal.service.loki.utilities.toHexString
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
@ -459,48 +458,6 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
val database = databaseHelper.writableDatabase
database.delete(closedGroupPublicKeysTable, "${Companion.groupPublicKey} = ?", wrap(groupPublicKey))
}
// region Deprecated
override fun getDeviceLinks(publicKey: String): Set<DeviceLink> {
return setOf()
/*
val database = databaseHelper.readableDatabase
return database.getAll(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey )) { cursor ->
val masterHexEncodedPublicKey = cursor.getString(masterPublicKey)
val slaveHexEncodedPublicKey = cursor.getString(slavePublicKey)
val requestSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(requestSignature))) null else cursor.getBase64EncodedData(requestSignature)
val authorizationSignature: ByteArray? = if (cursor.isNull(cursor.getColumnIndexOrThrow(authorizationSignature))) null else cursor.getBase64EncodedData(authorizationSignature)
DeviceLink(masterHexEncodedPublicKey, slaveHexEncodedPublicKey, requestSignature, authorizationSignature)
}.toSet()
*/
}
override fun clearDeviceLinks(publicKey: String) {
/*
val database = databaseHelper.writableDatabase
database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( publicKey, publicKey ))
*/
}
override fun addDeviceLink(deviceLink: DeviceLink) {
/*
val database = databaseHelper.writableDatabase
val values = ContentValues()
values.put(masterPublicKey, deviceLink.masterPublicKey)
values.put(slavePublicKey, deviceLink.slavePublicKey)
if (deviceLink.requestSignature != null) { values.put(requestSignature, Base64.encodeBytes(deviceLink.requestSignature)) }
if (deviceLink.authorizationSignature != null) { values.put(authorizationSignature, Base64.encodeBytes(deviceLink.authorizationSignature)) }
database.insertOrUpdate(deviceLinkCache, values, "$masterPublicKey = ? AND $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey ))
*/
}
override fun removeDeviceLink(deviceLink: DeviceLink) {
/*
val database = databaseHelper.writableDatabase
database.delete(deviceLinkCache, "$masterPublicKey = ? OR $slavePublicKey = ?", arrayOf( deviceLink.masterPublicKey, deviceLink.slavePublicKey ))
*/
}
// endregion
}
// region Convenience

View File

@ -13,7 +13,6 @@ import org.thoughtcrime.securesms.sms.OutgoingTextMessage
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.libsignal.loki.SessionResetStatus
import org.session.libsignal.service.api.messages.SignalServiceContent
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
import java.util.*
object SessionManagementProtocol {
@ -95,13 +94,12 @@ object SessionManagementProtocol {
@JvmStatic
fun triggerSessionRestorationUI(context: Context, publicKey: String, errorTimestamp: Long) {
val masterDevicePublicKey = MultiDeviceProtocol.shared.getMasterDevice(publicKey) ?: publicKey
val masterDeviceAsRecipient = recipient(context, masterDevicePublicKey)
if (masterDeviceAsRecipient.isGroupRecipient) { return }
val recipient = recipient(context, publicKey)
if (recipient.isGroupRecipient) { return }
if (TextSecurePreferences.getRestorationTime(context) > errorTimestamp) {
return ApplicationContext.getInstance(context).sendSessionRequestIfNeeded(publicKey)
}
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(masterDeviceAsRecipient)
val threadID = DatabaseFactory.getThreadDatabase(context).getOrCreateThreadIdFor(recipient)
DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey)
}
}

View File

@ -9,7 +9,6 @@ import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.JsonUtil
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
import org.session.libsignal.service.loki.utilities.*
import java.net.URL
import java.util.concurrent.ConcurrentHashMap
@ -17,21 +16,6 @@ import java.util.concurrent.ConcurrentHashMap
class FileServerAPI(public val server: String, userPublicKey: String, userPrivateKey: ByteArray, private val database: LokiAPIDatabaseProtocol) : DotNetAPI() {
companion object {
// region Settings
/**
* Deprecated.
*/
private val deviceLinkType = "network.loki.messenger.devicemapping"
/**
* Deprecated.
*/
private val deviceLinkRequestCache = ConcurrentHashMap<String, Promise<Set<DeviceLink>, Exception>>()
/**
* Deprecated.
*/
private val deviceLinkUpdateInterval = 60 * 1000
private val lastDeviceLinkUpdate = ConcurrentHashMap<String, Long>()
internal val fileServerPublicKey = "62509D59BDEEC404DD0D489C1E15BA8F94FD3D619B01C1BF48A9922BFCB7311C"
internal val maxRetryCount = 4
@ -63,172 +47,6 @@ class FileServerAPI(public val server: String, userPublicKey: String, userPrivat
// endregion
}
// region Device Link Update Result
sealed class DeviceLinkUpdateResult {
class Success(val publicKey: String, val deviceLinks: Set<DeviceLink>) : DeviceLinkUpdateResult()
class Failure(val publicKey: String, val error: Exception) : DeviceLinkUpdateResult()
}
// endregion
// region API
public fun hasDeviceLinkCacheExpired(referenceTime: Long = System.currentTimeMillis(), publicKey: String): Boolean {
return !lastDeviceLinkUpdate.containsKey(publicKey) || (referenceTime - lastDeviceLinkUpdate[publicKey]!! > deviceLinkUpdateInterval)
}
fun getDeviceLinks(publicKey: String, isForcedUpdate: Boolean = false): Promise<Set<DeviceLink>, Exception> {
return Promise.of(setOf())
/*
if (deviceLinkRequestCache.containsKey(publicKey) && !isForcedUpdate) {
val result = deviceLinkRequestCache[publicKey]
if (result != null) { return result } // A request was already pending
}
val promise = getDeviceLinks(setOf(publicKey), isForcedUpdate)
deviceLinkRequestCache[publicKey] = promise
promise.always {
deviceLinkRequestCache.remove(publicKey)
}
return promise
*/
}
fun getDeviceLinks(publicKeys: Set<String>, isForcedUpdate: Boolean = false): Promise<Set<DeviceLink>, Exception> {
return Promise.of(setOf())
/*
val validPublicKeys = publicKeys.filter { PublicKeyValidation.isValid(it) }
val now = System.currentTimeMillis()
// IMPORTANT: Don't fetch device links for the current user (i.e. don't remove the it != userHexEncodedPublicKey) check below
val updatees = validPublicKeys.filter { it != userPublicKey && (hasDeviceLinkCacheExpired(now, it) || isForcedUpdate) }.toSet()
val cachedDeviceLinks = validPublicKeys.minus(updatees).flatMap { database.getDeviceLinks(it) }.toSet()
if (updatees.isEmpty()) {
return Promise.of(cachedDeviceLinks)
} else {
return getUserProfiles(updatees, server, true).map(SnodeAPI.sharedContext) { data ->
data.map dataMap@ { node ->
val publicKey = node["username"] as String
val annotations = node["annotations"] as List<Map<*, *>>
val deviceLinksAnnotation = annotations.find {
annotation -> (annotation["type"] as String) == deviceLinkType
} ?: return@dataMap DeviceLinkUpdateResult.Success(publicKey, setOf())
val value = deviceLinksAnnotation["value"] as Map<*, *>
val deviceLinksAsJSON = value["authorisations"] as List<Map<*, *>>
val deviceLinks = deviceLinksAsJSON.mapNotNull { deviceLinkAsJSON ->
try {
val masterPublicKey = deviceLinkAsJSON["primaryDevicePubKey"] as String
val slavePublicKey = deviceLinkAsJSON["secondaryDevicePubKey"] as String
var requestSignature: ByteArray? = null
var authorizationSignature: ByteArray? = null
if (deviceLinkAsJSON["requestSignature"] != null) {
val base64EncodedSignature = deviceLinkAsJSON["requestSignature"] as String
requestSignature = Base64.decode(base64EncodedSignature)
}
if (deviceLinkAsJSON["grantSignature"] != null) {
val base64EncodedSignature = deviceLinkAsJSON["grantSignature"] as String
authorizationSignature = Base64.decode(base64EncodedSignature)
}
val deviceLink = DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature)
val isValid = deviceLink.verify()
if (!isValid) {
Log.d("Loki", "Ignoring invalid device link: $deviceLinkAsJSON.")
return@mapNotNull null
}
deviceLink
} catch (e: Exception) {
Log.d("Loki", "Failed to parse device links for $publicKey from $deviceLinkAsJSON due to error: $e.")
null
}
}.toSet()
DeviceLinkUpdateResult.Success(publicKey, deviceLinks)
}
}.recover { e ->
publicKeys.map { DeviceLinkUpdateResult.Failure(it, e) }
}.success { updateResults ->
for (updateResult in updateResults) {
if (updateResult is DeviceLinkUpdateResult.Success) {
database.clearDeviceLinks(updateResult.publicKey)
updateResult.deviceLinks.forEach { database.addDeviceLink(it) }
} else {
// Do nothing
}
}
}.map(SnodeAPI.sharedContext) { updateResults ->
val deviceLinks = mutableListOf<DeviceLink>()
for (updateResult in updateResults) {
when (updateResult) {
is DeviceLinkUpdateResult.Success -> {
lastDeviceLinkUpdate[updateResult.publicKey] = now
deviceLinks.addAll(updateResult.deviceLinks)
}
is DeviceLinkUpdateResult.Failure -> {
if (updateResult.error is SnodeAPI.Error.ParsingFailed) {
lastDeviceLinkUpdate[updateResult.publicKey] = now // Don't infinitely update in case of a parsing failure
}
deviceLinks.addAll(database.getDeviceLinks(updateResult.publicKey)) // Fall back on cached device links in case of a failure
}
}
}
// Updatees that didn't show up in the response provided by the file server are assumed to not have any device links
val excludedUpdatees = updatees.filter { updatee ->
updateResults.find { updateResult ->
when (updateResult) {
is DeviceLinkUpdateResult.Success -> updateResult.publicKey == updatee
is DeviceLinkUpdateResult.Failure -> updateResult.publicKey == updatee
}
} == null
}
excludedUpdatees.forEach {
lastDeviceLinkUpdate[it] = now
}
deviceLinks.union(cachedDeviceLinks)
}.recover {
publicKeys.flatMap { database.getDeviceLinks(it) }.toSet()
}
}
*/
}
fun setDeviceLinks(deviceLinks: Set<DeviceLink>): Promise<Unit, Exception> {
return Promise.of(Unit)
/*
val isMaster = deviceLinks.find { it.masterPublicKey == userPublicKey } != null
val deviceLinksAsJSON = deviceLinks.map { it.toJSON() }
val value = if (deviceLinks.isNotEmpty()) mapOf( "isPrimary" to isMaster, "authorisations" to deviceLinksAsJSON ) else null
val annotation = mapOf( "type" to deviceLinkType, "value" to value )
val parameters = mapOf( "annotations" to listOf( annotation ) )
return retryIfNeeded(maxRetryCount) {
execute(HTTPVerb.PATCH, server, "/users/me", parameters = parameters)
}.map { Unit }
*/
}
fun addDeviceLink(deviceLink: DeviceLink): Promise<Unit, Exception> {
return Promise.of(Unit)
/*
Log.d("Loki", "Updating device links.")
return getDeviceLinks(userPublicKey, true).bind { deviceLinks ->
val mutableDeviceLinks = deviceLinks.toMutableSet()
mutableDeviceLinks.add(deviceLink)
setDeviceLinks(mutableDeviceLinks)
}.success {
database.addDeviceLink(deviceLink)
}.map { Unit }
*/
}
fun removeDeviceLink(deviceLink: DeviceLink): Promise<Unit, Exception> {
return Promise.of(Unit)
/*
Log.d("Loki", "Updating device links.")
return getDeviceLinks(userPublicKey, true).bind { deviceLinks ->
val mutableDeviceLinks = deviceLinks.toMutableSet()
mutableDeviceLinks.remove(deviceLink)
setDeviceLinks(mutableDeviceLinks)
}.success {
database.removeDeviceLink(deviceLink)
}.map { Unit }
*/
}
// endregion
// region Open Group Server Public Key
fun getPublicKeyForOpenGroupServer(openGroupServer: String): Promise<String, Exception> {
val publicKey = database.getOpenGroupPublicKey(openGroupServer)

View File

@ -38,7 +38,6 @@ import org.session.libsession.utilities.Util;
import org.session.libsession.utilities.color.MaterialColor;
import org.session.libsignal.utilities.logging.Log;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
import org.session.libsession.messaging.avatars.ContactColors;
import org.session.libsession.messaging.avatars.ContactPhoto;
import org.session.libsession.messaging.avatars.GroupRecordContactPhoto;
@ -550,12 +549,7 @@ public class Recipient implements RecipientModifiedListener {
}
public synchronized boolean isBlocked() {
String masterPublicKey = MultiDeviceProtocol.shared.getMasterDevice(this.address.serialize());
if (masterPublicKey != null) {
return Recipient.from(context, Address.Companion.fromSerialized(masterPublicKey), false).blocked;
} else {
return blocked;
}
return blocked;
}
public void setBlocked(boolean blocked) {

View File

@ -78,8 +78,6 @@ import org.session.libsignal.service.loki.database.LokiUserDatabaseProtocol;
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.protocol.sessionmanagement.SessionManagementProtocol;
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol;
import org.session.libsignal.service.loki.protocol.shelved.syncmessages.SyncMessagesProtocol;
import org.session.libsignal.service.loki.utilities.Broadcaster;
import org.session.libsignal.service.loki.utilities.HexEncodingKt;
import org.session.libsignal.service.loki.utilities.PlaintextOutputStreamFactory;
@ -248,7 +246,7 @@ public class SignalServiceMessageSender {
long timestamp = message.getTimestamp();
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, recipient.getNumber(), store);
boolean isClosedGroup = message.group.isPresent() && message.group.get().getGroupType() == SignalServiceGroup.GroupType.SIGNAL;
SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), message.getDeviceLink().isPresent(), useFallbackEncryption, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
SendMessageResult result = sendMessage(messageID, recipient, getTargetUnidentifiedAccess(unidentifiedAccess), timestamp, content, false, message.getTTL(), useFallbackEncryption, isClosedGroup, message.hasVisibleContent(), message.getSyncTarget());
// // Loki - This shouldn't get invoked for note to self
// boolean wouldSignalSendSyncMessage = (result.getSuccess() != null && result.getSuccess().isNeedsSync()) || unidentifiedAccess.isPresent();
@ -298,18 +296,6 @@ public class SignalServiceMessageSender {
}
}
// Loki - This shouldn't get invoked for note to self
if (needsSyncInResults && SyncMessagesProtocol.shared.shouldSyncMessage(message)) {
byte[] syncMessage = createMultiDeviceSentTranscriptContent(content, Optional.<SignalServiceAddress>absent(), timestamp, results);
// Loki - Customize multi device logic
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
for (String device : linkedDevices) {
SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device);
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(syncMessage, device, store);
sendMessage(deviceAsAddress, Optional.<UnidentifiedAccess>absent(), timestamp, syncMessage, false, message.getTTL(), useFallbackEncryption);
}
}
return results;
}
@ -341,14 +327,6 @@ public class SignalServiceMessageSender {
} else {
throw new IOException("Unsupported sync message!");
}
// Loki - Customize multi device logic
Set<String> linkedDevices = MultiDeviceProtocol.shared.getAllLinkedDevices(userPublicKey);
for (String device : linkedDevices) {
SignalServiceAddress deviceAsAddress = new SignalServiceAddress(device);
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(message, device, store);
sendMessageToPrivateChat(0, deviceAsAddress, Optional.absent(), timestamp, content, false, message.getTTL(), useFallbackEncryption, false, false, Optional.absent());
}
}
public void setMessagePipe(SignalServiceMessagePipe pipe, SignalServiceMessagePipe unidentifiedPipe) {
@ -462,10 +440,6 @@ public class SignalServiceMessageSender {
builder.setFlags(DataMessage.Flags.PROFILE_KEY_UPDATE_VALUE);
}
if (message.isDeviceUnlinkingRequest()) {
builder.setFlags(DataMessage.Flags.DEVICE_UNLINKING_REQUEST_VALUE);
}
if (message.getExpiresInSeconds() > 0) {
builder.setExpireTimer(message.getExpiresInSeconds());
}
@ -881,7 +855,7 @@ public class SignalServiceMessageSender {
try {
boolean useFallbackEncryption = SessionManagementProtocol.shared.shouldMessageUseFallbackEncryption(content, recipient.getNumber(), store);
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, false, useFallbackEncryption, isClosedGroup, notifyPNServer, Optional.absent());
SendMessageResult result = sendMessage(messageID, recipient, unidentifiedAccessIterator.next(), timestamp, content, online, ttl, useFallbackEncryption, isClosedGroup, notifyPNServer, Optional.absent());
results.add(result);
} catch (UnregisteredUserException e) {
Log.w(TAG, e);
@ -905,7 +879,7 @@ public class SignalServiceMessageSender {
throws IOException
{
// Loki - This method is only invoked for various types of control messages
return sendMessage(0, recipient, unidentifiedAccess, timestamp, content, online, ttl, false, false, useFallbackEncryption, false,Optional.absent());
return sendMessage(0, recipient, unidentifiedAccess, timestamp, content, online, ttl, false, useFallbackEncryption, false,Optional.absent());
}
public SendMessageResult sendMessage(final long messageID,
@ -915,7 +889,6 @@ public class SignalServiceMessageSender {
byte[] content,
boolean online,
int ttl,
boolean isDeviceLinkMessage,
boolean useFallbackEncryption,
boolean isClosedGroup,
boolean notifyPNServer,

View File

@ -91,7 +91,6 @@ import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol;
import org.session.libsignal.service.loki.protocol.closedgroups.ClosedGroupUtilities;
import org.session.libsignal.service.loki.protocol.closedgroups.SharedSenderKeysDatabaseProtocol;
import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage;
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink;
import java.io.ByteArrayInputStream;
import java.io.IOException;
@ -226,30 +225,6 @@ public class SignalServiceCipher {
content.setDataMessage(signalServiceDataMessage);
}
return content;
} else if (message.hasDeviceLinkMessage()) {
SignalServiceProtos.DeviceLinkMessage protoDeviceLinkMessage = message.getDeviceLinkMessage();
String masterPublicKey = protoDeviceLinkMessage.getPrimaryPublicKey();
String slavePublicKey = protoDeviceLinkMessage.getSecondaryPublicKey();
byte[] requestSignature = protoDeviceLinkMessage.hasRequestSignature() ? protoDeviceLinkMessage.getRequestSignature().toByteArray() : null;
byte[] authorizationSignature = protoDeviceLinkMessage.hasAuthorizationSignature() ? protoDeviceLinkMessage.getAuthorizationSignature().toByteArray() : null;
DeviceLink deviceLink = new DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature);
SignalServiceCipher.Metadata metadata = plaintext.getMetadata();
SignalServiceContent content = new SignalServiceContent(deviceLink, metadata.getSender(), metadata.getSenderDevice(), metadata.getTimestamp());
content.setPreKeyBundleMessage(preKeyBundleMessage);
if (message.hasSyncMessage() && message.getSyncMessage().hasContacts()) {
SignalServiceSyncMessage syncMessage = createSynchronizeMessage(metadata, message.getSyncMessage());
content.setSyncMessage(syncMessage);
}
if (message.hasDataMessage()) {
setProfile(message.getDataMessage(), content);
SignalServiceDataMessage signalServiceDataMessage = createSignalServiceMessage(metadata, message.getDataMessage());
content.setDataMessage(signalServiceDataMessage);
}
return content;
} else if (message.hasDataMessage()) {
DataMessage dataMessage = message.getDataMessage();
@ -259,8 +234,7 @@ public class SignalServiceCipher {
plaintext.getMetadata().getSender(),
plaintext.getMetadata().getSenderDevice(),
plaintext.getMetadata().getTimestamp(),
plaintext.getMetadata().isNeedsReceipt(),
signalServiceDataMessage.isDeviceUnlinkingRequest());
plaintext.getMetadata().isNeedsReceipt());
content.setPreKeyBundleMessage(preKeyBundleMessage);
@ -425,10 +399,8 @@ public class SignalServiceCipher {
previews,
sticker,
null,
null,
closedGroupUpdate,
closedGroupUpdateV2,
isDeviceUnlinkingRequest,
syncTarget);
}

View File

@ -7,15 +7,9 @@
package org.session.libsignal.service.api.messages;
import org.session.libsignal.libsignal.util.guava.Optional;
import org.session.libsignal.service.api.messages.SignalServiceDataMessage;
import org.session.libsignal.service.api.messages.SignalServiceNullMessage;
import org.session.libsignal.service.api.messages.SignalServiceReceiptMessage;
import org.session.libsignal.service.api.messages.SignalServiceTypingMessage;
import org.session.libsignal.service.api.messages.calls.SignalServiceCallMessage;
import org.session.libsignal.service.api.messages.multidevice.ConfigurationMessage;
import org.session.libsignal.service.api.messages.multidevice.SignalServiceSyncMessage;
import org.session.libsignal.service.internal.push.SignalServiceProtos;
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink;
import org.session.libsignal.service.loki.protocol.sessionmanagement.PreKeyBundleMessage;
public class SignalServiceContent {
@ -25,8 +19,6 @@ public class SignalServiceContent {
private final boolean needsReceipt;
// Loki
private final boolean isDeviceUnlinkingRequest;
private Optional<SignalServiceDataMessage> message;
private Optional<SignalServiceSyncMessage> synchronizeMessage;
private final Optional<SignalServiceCallMessage> callMessage;
@ -35,13 +27,12 @@ public class SignalServiceContent {
private final Optional<SignalServiceTypingMessage> typingMessage;
// Loki
private final Optional<DeviceLink> deviceLink;
public Optional<SignalServiceProtos.Content> configurationMessageProto = Optional.absent();
public Optional<PreKeyBundleMessage> preKeyBundleMessage = Optional.absent();
public Optional<String> senderDisplayName = Optional.absent();
public Optional<String> senderProfilePictureURL = Optional.absent();
public SignalServiceContent(SignalServiceDataMessage message, String sender, int senderDevice, long timestamp, boolean needsReceipt, boolean isDeviceUnlinkingRequest) {
public SignalServiceContent(SignalServiceDataMessage message, String sender, int senderDevice, long timestamp, boolean needsReceipt) {
this.sender = sender;
this.senderDevice = senderDevice;
this.timestamp = timestamp;
@ -52,8 +43,6 @@ public class SignalServiceContent {
this.nullMessage = Optional.absent();
this.readMessage = Optional.absent();
this.typingMessage = Optional.absent();
this.deviceLink = Optional.absent();
this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest;
}
public SignalServiceContent(SignalServiceSyncMessage synchronizeMessage, String sender, int senderDevice, long timestamp) {
@ -67,8 +56,6 @@ public class SignalServiceContent {
this.nullMessage = Optional.absent();
this.readMessage = Optional.absent();
this.typingMessage = Optional.absent();
this.deviceLink = Optional.absent();
this.isDeviceUnlinkingRequest = false;
}
public SignalServiceContent(SignalServiceCallMessage callMessage, String sender, int senderDevice, long timestamp) {
@ -82,8 +69,6 @@ public class SignalServiceContent {
this.nullMessage = Optional.absent();
this.readMessage = Optional.absent();
this.typingMessage = Optional.absent();
this.deviceLink = Optional.absent();
this.isDeviceUnlinkingRequest = false;
}
public SignalServiceContent(SignalServiceReceiptMessage receiptMessage, String sender, int senderDevice, long timestamp) {
@ -97,8 +82,6 @@ public class SignalServiceContent {
this.nullMessage = Optional.absent();
this.readMessage = Optional.of(receiptMessage);
this.typingMessage = Optional.absent();
this.deviceLink = Optional.absent();
this.isDeviceUnlinkingRequest = false;
}
public SignalServiceContent(SignalServiceTypingMessage typingMessage, String sender, int senderDevice, long timestamp) {
@ -112,23 +95,6 @@ public class SignalServiceContent {
this.nullMessage = Optional.absent();
this.readMessage = Optional.absent();
this.typingMessage = Optional.of(typingMessage);
this.deviceLink = Optional.absent();
this.isDeviceUnlinkingRequest = false;
}
public SignalServiceContent(DeviceLink deviceLink, String sender, int senderDevice, long timestamp) {
this.sender = sender;
this.senderDevice = senderDevice;
this.timestamp = timestamp;
this.needsReceipt = false;
this.message = Optional.absent();
this.synchronizeMessage = Optional.absent();
this.callMessage = Optional.absent();
this.nullMessage = Optional.absent();
this.readMessage = Optional.absent();
this.typingMessage = Optional.absent();
this.deviceLink = Optional.fromNullable(deviceLink);
this.isDeviceUnlinkingRequest = false;
}
public SignalServiceContent(SignalServiceProtos.Content configurationMessageProto, String sender, int senderDevice, long timestamp) {
@ -142,9 +108,7 @@ public class SignalServiceContent {
this.nullMessage = Optional.absent();
this.readMessage = Optional.absent();
this.typingMessage = Optional.absent();
this.deviceLink = Optional.absent();
this.configurationMessageProto = Optional.fromNullable(configurationMessageProto);
this.isDeviceUnlinkingRequest = false;
}
public SignalServiceContent(SignalServiceNullMessage nullMessage, String sender, int senderDevice, long timestamp) {
@ -158,8 +122,6 @@ public class SignalServiceContent {
this.nullMessage = Optional.of(nullMessage);
this.readMessage = Optional.absent();
this.typingMessage = Optional.absent();
this.deviceLink = Optional.absent();
this.isDeviceUnlinkingRequest = false;
}
public Optional<SignalServiceDataMessage> getDataMessage() {
@ -203,9 +165,6 @@ public class SignalServiceContent {
public Optional<SignalServiceNullMessage> getNullMessage() { return nullMessage; }
// Loki
public boolean isDeviceUnlinkingRequest() { return isDeviceUnlinkingRequest; }
public Optional<DeviceLink> getDeviceLink() { return deviceLink; }
public void setPreKeyBundleMessage(PreKeyBundleMessage preKeyBundleMessage) { this.preKeyBundleMessage = Optional.fromNullable(preKeyBundleMessage); }

View File

@ -13,7 +13,6 @@ import org.session.libsignal.service.api.push.SignalServiceAddress;
import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdateV2;
import org.session.libsignal.service.internal.push.SignalServiceProtos.ClosedGroupUpdate;
import org.session.libsignal.service.loki.protocol.meta.TTLUtilities;
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink;
import java.util.LinkedList;
import java.util.List;
@ -37,10 +36,8 @@ public class SignalServiceDataMessage {
private final Optional<Sticker> sticker;
// Loki
private final Optional<PreKeyBundle> preKeyBundle;
private final Optional<DeviceLink> deviceLink;
private final Optional<ClosedGroupUpdate> closedGroupUpdate;
private final Optional<ClosedGroupUpdateV2> closedGroupUpdateV2;
private final boolean isDeviceUnlinkingRequest;
private final Optional<String> syncTarget;
/**
@ -135,7 +132,7 @@ public class SignalServiceDataMessage {
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
Sticker sticker)
{
this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, null, false, null);
this(timestamp, group, attachments, body, endSession, expiresInSeconds, expirationUpdate, profileKey, profileKeyUpdate, quote, sharedContacts, previews, sticker, null, null, null, null);
}
/**
@ -154,9 +151,9 @@ public class SignalServiceDataMessage {
String body, boolean endSession, int expiresInSeconds,
boolean expirationUpdate, byte[] profileKey, boolean profileKeyUpdate,
Quote quote, List<SharedContact> sharedContacts, List<Preview> previews,
Sticker sticker, PreKeyBundle preKeyBundle, DeviceLink deviceLink,
Sticker sticker, PreKeyBundle preKeyBundle,
ClosedGroupUpdate closedGroupUpdate, ClosedGroupUpdateV2 closedGroupUpdateV2,
boolean isDeviceUnlinkingRequest, String syncTarget)
String syncTarget)
{
this.timestamp = timestamp;
this.body = Optional.fromNullable(body);
@ -169,10 +166,8 @@ public class SignalServiceDataMessage {
this.quote = Optional.fromNullable(quote);
this.sticker = Optional.fromNullable(sticker);
this.preKeyBundle = Optional.fromNullable(preKeyBundle);
this.deviceLink = Optional.fromNullable(deviceLink);
this.closedGroupUpdate = Optional.fromNullable(closedGroupUpdate);
this.closedGroupUpdateV2 = Optional.fromNullable(closedGroupUpdateV2);
this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest;
this.syncTarget = Optional.fromNullable(syncTarget);
if (attachments != null && !attachments.isEmpty()) {
@ -273,26 +268,18 @@ public class SignalServiceDataMessage {
}
// Loki
public boolean isDeviceUnlinkingRequest() {
return isDeviceUnlinkingRequest;
}
public Optional<ClosedGroupUpdate> getClosedGroupUpdate() { return closedGroupUpdate; }
public Optional<ClosedGroupUpdateV2> getClosedGroupUpdateV2() { return closedGroupUpdateV2; }
public Optional<PreKeyBundle> getPreKeyBundle() { return preKeyBundle; }
public Optional<DeviceLink> getDeviceLink() { return deviceLink; }
public boolean hasVisibleContent() {
return (body.isPresent() && !body.get().isEmpty())
|| (attachments.isPresent() && !attachments.get().isEmpty());
}
public int getTTL() {
if (deviceLink.isPresent()) { return TTLUtilities.getTTL(TTLUtilities.MessageType.DeviceLink); }
else if (isDeviceUnlinkingRequest) { return TTLUtilities.getTTL(TTLUtilities.MessageType.DeviceUnlinkingRequest); }
return TTLUtilities.getTTL(TTLUtilities.MessageType.Regular);
}
@ -312,9 +299,7 @@ public class SignalServiceDataMessage {
private Quote quote;
private Sticker sticker;
private PreKeyBundle preKeyBundle;
private DeviceLink deviceLink;
private String syncTarget;
private boolean isDeviceUnlinkingRequest;
private Builder() {}
@ -411,25 +396,14 @@ public class SignalServiceDataMessage {
return this;
}
public Builder withDeviceLink(DeviceLink deviceLink) {
this.deviceLink = deviceLink;
return this;
}
public Builder asDeviceUnlinkingRequest(boolean isDeviceUnlinkingRequest) {
this.isDeviceUnlinkingRequest = isDeviceUnlinkingRequest;
return this;
}
public SignalServiceDataMessage build() {
if (timestamp == 0) timestamp = System.currentTimeMillis();
// closedGroupUpdate is always null because we don't use SignalServiceDataMessage to send them (we use ClosedGroupUpdateMessageSendJob)
return new SignalServiceDataMessage(timestamp, group, attachments, body, endSession,
expiresInSeconds, expirationUpdate, profileKey,
profileKeyUpdate, quote, sharedContacts, previews,
sticker, preKeyBundle, deviceLink,
null, null,
isDeviceUnlinkingRequest, syncTarget);
sticker, preKeyBundle, null, null,
syncTarget);
}
}

View File

@ -9,7 +9,6 @@ import org.session.libsignal.utilities.JsonUtil
import org.session.libsignal.service.loki.api.LokiDotNetAPI
import org.session.libsignal.service.loki.api.onionrequests.OnionRequestAPI
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
import org.session.libsignal.service.loki.utilities.*
import java.net.URL
import java.util.concurrent.ConcurrentHashMap
@ -18,20 +17,6 @@ class FileServerAPI(public val server: String, userPublicKey: String, userPrivat
companion object {
// region Settings
/**
* Deprecated.
*/
private val deviceLinkType = "network.loki.messenger.devicemapping"
/**
* Deprecated.
*/
private val deviceLinkRequestCache = ConcurrentHashMap<String, Promise<Set<DeviceLink>, Exception>>()
/**
* Deprecated.
*/
private val deviceLinkUpdateInterval = 60 * 1000
private val lastDeviceLinkUpdate = ConcurrentHashMap<String, Long>()
internal val fileServerPublicKey = "62509D59BDEEC404DD0D489C1E15BA8F94FD3D619B01C1BF48A9922BFCB7311C"
internal val maxRetryCount = 4
@ -62,172 +47,6 @@ class FileServerAPI(public val server: String, userPublicKey: String, userPrivat
// endregion
}
// region Device Link Update Result
sealed class DeviceLinkUpdateResult {
class Success(val publicKey: String, val deviceLinks: Set<DeviceLink>) : DeviceLinkUpdateResult()
class Failure(val publicKey: String, val error: Exception) : DeviceLinkUpdateResult()
}
// endregion
// region API
public fun hasDeviceLinkCacheExpired(referenceTime: Long = System.currentTimeMillis(), publicKey: String): Boolean {
return !lastDeviceLinkUpdate.containsKey(publicKey) || (referenceTime - lastDeviceLinkUpdate[publicKey]!! > deviceLinkUpdateInterval)
}
fun getDeviceLinks(publicKey: String, isForcedUpdate: Boolean = false): Promise<Set<DeviceLink>, Exception> {
return Promise.of(setOf())
/*
if (deviceLinkRequestCache.containsKey(publicKey) && !isForcedUpdate) {
val result = deviceLinkRequestCache[publicKey]
if (result != null) { return result } // A request was already pending
}
val promise = getDeviceLinks(setOf(publicKey), isForcedUpdate)
deviceLinkRequestCache[publicKey] = promise
promise.always {
deviceLinkRequestCache.remove(publicKey)
}
return promise
*/
}
fun getDeviceLinks(publicKeys: Set<String>, isForcedUpdate: Boolean = false): Promise<Set<DeviceLink>, Exception> {
return Promise.of(setOf())
/*
val validPublicKeys = publicKeys.filter { PublicKeyValidation.isValid(it) }
val now = System.currentTimeMillis()
// IMPORTANT: Don't fetch device links for the current user (i.e. don't remove the it != userHexEncodedPublicKey) check below
val updatees = validPublicKeys.filter { it != userPublicKey && (hasDeviceLinkCacheExpired(now, it) || isForcedUpdate) }.toSet()
val cachedDeviceLinks = validPublicKeys.minus(updatees).flatMap { database.getDeviceLinks(it) }.toSet()
if (updatees.isEmpty()) {
return Promise.of(cachedDeviceLinks)
} else {
return getUserProfiles(updatees, server, true).map(SnodeAPI.sharedContext) { data ->
data.map dataMap@ { node ->
val publicKey = node["username"] as String
val annotations = node["annotations"] as List<Map<*, *>>
val deviceLinksAnnotation = annotations.find {
annotation -> (annotation["type"] as String) == deviceLinkType
} ?: return@dataMap DeviceLinkUpdateResult.Success(publicKey, setOf())
val value = deviceLinksAnnotation["value"] as Map<*, *>
val deviceLinksAsJSON = value["authorisations"] as List<Map<*, *>>
val deviceLinks = deviceLinksAsJSON.mapNotNull { deviceLinkAsJSON ->
try {
val masterPublicKey = deviceLinkAsJSON["primaryDevicePubKey"] as String
val slavePublicKey = deviceLinkAsJSON["secondaryDevicePubKey"] as String
var requestSignature: ByteArray? = null
var authorizationSignature: ByteArray? = null
if (deviceLinkAsJSON["requestSignature"] != null) {
val base64EncodedSignature = deviceLinkAsJSON["requestSignature"] as String
requestSignature = Base64.decode(base64EncodedSignature)
}
if (deviceLinkAsJSON["grantSignature"] != null) {
val base64EncodedSignature = deviceLinkAsJSON["grantSignature"] as String
authorizationSignature = Base64.decode(base64EncodedSignature)
}
val deviceLink = DeviceLink(masterPublicKey, slavePublicKey, requestSignature, authorizationSignature)
val isValid = deviceLink.verify()
if (!isValid) {
Log.d("Loki", "Ignoring invalid device link: $deviceLinkAsJSON.")
return@mapNotNull null
}
deviceLink
} catch (e: Exception) {
Log.d("Loki", "Failed to parse device links for $publicKey from $deviceLinkAsJSON due to error: $e.")
null
}
}.toSet()
DeviceLinkUpdateResult.Success(publicKey, deviceLinks)
}
}.recover { e ->
publicKeys.map { DeviceLinkUpdateResult.Failure(it, e) }
}.success { updateResults ->
for (updateResult in updateResults) {
if (updateResult is DeviceLinkUpdateResult.Success) {
database.clearDeviceLinks(updateResult.publicKey)
updateResult.deviceLinks.forEach { database.addDeviceLink(it) }
} else {
// Do nothing
}
}
}.map(SnodeAPI.sharedContext) { updateResults ->
val deviceLinks = mutableListOf<DeviceLink>()
for (updateResult in updateResults) {
when (updateResult) {
is DeviceLinkUpdateResult.Success -> {
lastDeviceLinkUpdate[updateResult.publicKey] = now
deviceLinks.addAll(updateResult.deviceLinks)
}
is DeviceLinkUpdateResult.Failure -> {
if (updateResult.error is SnodeAPI.Error.ParsingFailed) {
lastDeviceLinkUpdate[updateResult.publicKey] = now // Don't infinitely update in case of a parsing failure
}
deviceLinks.addAll(database.getDeviceLinks(updateResult.publicKey)) // Fall back on cached device links in case of a failure
}
}
}
// Updatees that didn't show up in the response provided by the file server are assumed to not have any device links
val excludedUpdatees = updatees.filter { updatee ->
updateResults.find { updateResult ->
when (updateResult) {
is DeviceLinkUpdateResult.Success -> updateResult.publicKey == updatee
is DeviceLinkUpdateResult.Failure -> updateResult.publicKey == updatee
}
} == null
}
excludedUpdatees.forEach {
lastDeviceLinkUpdate[it] = now
}
deviceLinks.union(cachedDeviceLinks)
}.recover {
publicKeys.flatMap { database.getDeviceLinks(it) }.toSet()
}
}
*/
}
fun setDeviceLinks(deviceLinks: Set<DeviceLink>): Promise<Unit, Exception> {
return Promise.of(Unit)
/*
val isMaster = deviceLinks.find { it.masterPublicKey == userPublicKey } != null
val deviceLinksAsJSON = deviceLinks.map { it.toJSON() }
val value = if (deviceLinks.isNotEmpty()) mapOf( "isPrimary" to isMaster, "authorisations" to deviceLinksAsJSON ) else null
val annotation = mapOf( "type" to deviceLinkType, "value" to value )
val parameters = mapOf( "annotations" to listOf( annotation ) )
return retryIfNeeded(maxRetryCount) {
execute(HTTPVerb.PATCH, server, "/users/me", parameters = parameters)
}.map { Unit }
*/
}
fun addDeviceLink(deviceLink: DeviceLink): Promise<Unit, Exception> {
return Promise.of(Unit)
/*
Log.d("Loki", "Updating device links.")
return getDeviceLinks(userPublicKey, true).bind { deviceLinks ->
val mutableDeviceLinks = deviceLinks.toMutableSet()
mutableDeviceLinks.add(deviceLink)
setDeviceLinks(mutableDeviceLinks)
}.success {
database.addDeviceLink(deviceLink)
}.map { Unit }
*/
}
fun removeDeviceLink(deviceLink: DeviceLink): Promise<Unit, Exception> {
return Promise.of(Unit)
/*
Log.d("Loki", "Updating device links.")
return getDeviceLinks(userPublicKey, true).bind { deviceLinks ->
val mutableDeviceLinks = deviceLinks.toMutableSet()
mutableDeviceLinks.remove(deviceLink)
setDeviceLinks(mutableDeviceLinks)
}.success {
database.removeDeviceLink(deviceLink)
}.map { Unit }
*/
}
// endregion
// region Open Group Server Public Key
fun getPublicKeyForOpenGroupServer(openGroupServer: String): Promise<String, Exception> {
val publicKey = database.getOpenGroupPublicKey(openGroupServer)

View File

@ -2,7 +2,6 @@ package org.session.libsignal.service.loki.database
import org.session.libsignal.libsignal.ecc.ECKeyPair
import org.session.libsignal.service.loki.api.Snode
import org.session.libsignal.service.loki.protocol.shelved.multidevice.DeviceLink
import java.util.*
interface LokiAPIDatabaseProtocol {
@ -38,11 +37,4 @@ interface LokiAPIDatabaseProtocol {
fun getUserX25519KeyPair(): ECKeyPair
fun getClosedGroupEncryptionKeyPairs(groupPublicKey: String): List<ECKeyPair>
fun getLatestClosedGroupEncryptionKeyPair(groupPublicKey: String): ECKeyPair?
// region Deprecated
fun getDeviceLinks(publicKey: String): Set<DeviceLink>
fun clearDeviceLinks(publicKey: String)
fun addDeviceLink(deviceLink: DeviceLink)
fun removeDeviceLink(deviceLink: DeviceLink)
// endregion
}

View File

@ -1,7 +1,6 @@
package org.session.libsignal.service.loki.protocol.meta
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
import org.session.libsignal.service.loki.protocol.shelved.multidevice.MultiDeviceProtocol
public class SessionMetaProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) {

View File

@ -1,72 +0,0 @@
package org.session.libsignal.service.loki.protocol.shelved.multidevice
import org.whispersystems.curve25519.Curve25519
import org.session.libsignal.utilities.logging.Log
import org.session.libsignal.utilities.Base64
import org.session.libsignal.utilities.Hex
import org.session.libsignal.service.loki.utilities.removing05PrefixIfNeeded
import java.util.*
data class DeviceLink(val masterPublicKey: String, val slavePublicKey: String, val requestSignature: ByteArray?, val authorizationSignature: ByteArray?) {
private val curve = Curve25519.getInstance(Curve25519.BEST)
val type: Type
get() = when (authorizationSignature) {
null -> Type.REQUEST
else -> Type.AUTHORIZATION
}
enum class Type(val rawValue: Int) { REQUEST(1), AUTHORIZATION(2) }
constructor(masterPublicKey: String, slavePublicKey: String) : this(masterPublicKey, slavePublicKey, null, null)
fun sign(type: Type, privateKey: ByteArray): DeviceLink? {
val target = if (type == Type.REQUEST) masterPublicKey else slavePublicKey
val data = Hex.fromStringCondensed(target) + ByteArray(1) { type.rawValue.toByte() }
try {
val signature = curve.calculateSignature(privateKey, data)
return if (type == Type.REQUEST) copy(requestSignature = signature) else copy(authorizationSignature = signature)
} catch (e: Exception) {
return null
}
}
fun verify(): Boolean {
if (requestSignature == null && authorizationSignature == null) { return false }
val signature = if (type == Type.REQUEST) requestSignature else authorizationSignature
val issuer = if (type == Type.REQUEST) slavePublicKey else masterPublicKey
val target = if (type == Type.REQUEST) masterPublicKey else slavePublicKey
return try {
val data = Hex.fromStringCondensed(target) + ByteArray(1) { type.rawValue.toByte() }
val issuerPublicKey = Hex.fromStringCondensed(issuer.removing05PrefixIfNeeded())
curve.verifySignature(issuerPublicKey, data, signature)
} catch (e: Exception) {
Log.w("LOKI", e.message)
false
}
}
fun toJSON(): Map<String, Any> {
val result = mutableMapOf( "primaryDevicePubKey" to masterPublicKey, "secondaryDevicePubKey" to slavePublicKey )
if (requestSignature != null) { result["requestSignature"] = Base64.encodeBytes(requestSignature) }
if (authorizationSignature != null) { result["grantSignature"] = Base64.encodeBytes(authorizationSignature) }
return result
}
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other is DeviceLink) {
return (masterPublicKey == other.masterPublicKey && slavePublicKey == other.slavePublicKey
&& Arrays.equals(requestSignature, other.requestSignature) && Arrays.equals(authorizationSignature, other.authorizationSignature))
} else {
return false
}
}
override fun hashCode(): Int {
var hash = masterPublicKey.hashCode() xor slavePublicKey.hashCode()
if (requestSignature != null) { hash = hash xor Arrays.hashCode(requestSignature) }
if (authorizationSignature != null) { hash = hash xor Arrays.hashCode(authorizationSignature) }
return hash
}
}

View File

@ -1,37 +0,0 @@
package org.session.libsignal.service.loki.protocol.shelved.multidevice
class DeviceLinkingSession {
private val listeners = mutableListOf<DeviceLinkingSessionListener>()
var isListeningForLinkingRequests: Boolean = false
private set
companion object {
val shared = DeviceLinkingSession()
}
fun addListener(listener: DeviceLinkingSessionListener) {
listeners.add(listener)
}
fun removeListener(listener: DeviceLinkingSessionListener) {
listeners.remove(listener)
}
fun startListeningForLinkingRequests() {
isListeningForLinkingRequests = true
}
fun stopListeningForLinkingRequests() {
isListeningForLinkingRequests = false
}
fun processLinkingRequest(deviceLink: DeviceLink) {
if (!isListeningForLinkingRequests || !deviceLink.verify()) { return }
listeners.forEach { it.requestUserAuthorization(deviceLink) }
}
fun processLinkingAuthorization(deviceLink: DeviceLink) {
if (!isListeningForLinkingRequests || !deviceLink.verify()) { return }
listeners.forEach { it.onDeviceLinkRequestAuthorized(deviceLink) }
}
}

View File

@ -1,7 +0,0 @@
package org.session.libsignal.service.loki.protocol.shelved.multidevice
interface DeviceLinkingSessionListener {
fun requestUserAuthorization(deviceLink: DeviceLink) { }
fun onDeviceLinkRequestAuthorized(deviceLink: DeviceLink) { }
}

View File

@ -1,46 +0,0 @@
package org.session.libsignal.service.loki.protocol.shelved.multidevice
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
public class MultiDeviceProtocol(private val apiDatabase: LokiAPIDatabaseProtocol) {
// region Initialization
companion object {
public lateinit var shared: MultiDeviceProtocol
public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol) {
if (Companion::shared.isInitialized) { return; }
shared = MultiDeviceProtocol(apiDatabase)
}
}
// endregion
// region Utilities
public fun getMasterDevice(publicKey: String): String? {
return null
/*
val deviceLinks = apiDatabase.getDeviceLinks(publicKey)
return deviceLinks.firstOrNull { it.slavePublicKey == publicKey }?.masterPublicKey
*/
}
public fun getSlaveDevices(publicKey: String): Set<String> {
return setOf()
/*
val deviceLinks = apiDatabase.getDeviceLinks(publicKey)
if (deviceLinks.isEmpty()) { return setOf() }
return deviceLinks.map { it.slavePublicKey }.toSet()
*/
}
public fun getAllLinkedDevices(publicKey: String): Set<String> {
return setOf( publicKey )
/*
val deviceLinks = apiDatabase.getDeviceLinks(publicKey)
if (deviceLinks.isEmpty()) { return setOf( publicKey ) }
return deviceLinks.flatMap { listOf( it.masterPublicKey, it.slavePublicKey ) }.toSet()
*/
}
// endregion
}

View File

@ -1,36 +0,0 @@
package org.session.libsignal.service.loki.protocol.shelved.syncmessages
import org.session.libsignal.service.api.messages.SignalServiceDataMessage
import org.session.libsignal.service.api.messages.SignalServiceGroup
import org.session.libsignal.service.loki.database.LokiAPIDatabaseProtocol
public class SyncMessagesProtocol(private val apiDatabase: LokiAPIDatabaseProtocol, private val userPublicKey: String) {
// region Initialization
companion object {
public lateinit var shared: SyncMessagesProtocol
public fun configureIfNeeded(apiDatabase: LokiAPIDatabaseProtocol, userPublicKey: String) {
if (Companion::shared.isInitialized) { return; }
shared = SyncMessagesProtocol(apiDatabase, userPublicKey)
}
}
// endregion
// region Sending
/**
* Note: This is called only if based on Signal's logic we'd want to send a sync message.
*/
public fun shouldSyncMessage(message: SignalServiceDataMessage): Boolean {
return false
/*
if (message.deviceLink.isPresent) { return false }
val isOpenGroupMessage = message.group.isPresent && message.group.get().groupType == SignalServiceGroup.GroupType.PUBLIC_CHAT
if (isOpenGroupMessage) { return false }
val usesMultiDevice = apiDatabase.getDeviceLinks(userPublicKey).isNotEmpty()
return usesMultiDevice
*/
}
// endregion
}