2020-05-11 16:54:31 +10:00
|
|
|
package org.thoughtcrime.securesms.loki.protocol
|
|
|
|
|
|
|
|
import android.content.Context
|
|
|
|
import android.util.Log
|
|
|
|
import org.thoughtcrime.securesms.ApplicationContext
|
|
|
|
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
|
|
|
|
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
2020-05-13 12:29:31 +10:00
|
|
|
import org.thoughtcrime.securesms.crypto.SecurityEvent
|
|
|
|
import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore
|
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory
|
2020-05-11 16:54:31 +10:00
|
|
|
import org.thoughtcrime.securesms.jobs.CleanPreKeysJob
|
2020-05-13 12:29:31 +10:00
|
|
|
import org.thoughtcrime.securesms.loki.utilities.recipient
|
2020-05-14 11:19:20 +10:00
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient
|
2020-07-15 15:14:37 +10:00
|
|
|
import org.thoughtcrime.securesms.sms.MessageSender
|
|
|
|
import org.thoughtcrime.securesms.sms.OutgoingEndSessionMessage
|
2020-05-14 11:19:20 +10:00
|
|
|
import org.thoughtcrime.securesms.sms.OutgoingTextMessage
|
2020-05-11 16:54:31 +10:00
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences
|
2020-07-15 12:24:43 +10:00
|
|
|
import org.whispersystems.libsignal.loki.SessionResetStatus
|
2020-05-13 12:29:31 +10:00
|
|
|
import org.whispersystems.signalservice.api.messages.SignalServiceContent
|
2020-08-04 15:33:37 +10:00
|
|
|
import org.whispersystems.signalservice.loki.protocol.shelved.multidevice.MultiDeviceProtocol
|
2020-07-10 09:46:14 +10:00
|
|
|
import java.util.*
|
2020-05-13 12:29:31 +10:00
|
|
|
|
2020-05-11 16:54:31 +10:00
|
|
|
object SessionManagementProtocol {
|
|
|
|
|
2020-05-14 10:44:13 +10:00
|
|
|
@JvmStatic
|
2020-05-14 11:19:20 +10:00
|
|
|
fun startSessionReset(context: Context, recipient: Recipient, threadID: Long) {
|
|
|
|
if (recipient.isGroupRecipient) { return }
|
|
|
|
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
|
|
|
|
val smsDB = DatabaseFactory.getSmsDatabase(context)
|
|
|
|
val devices = lokiThreadDB.getSessionRestoreDevices(threadID)
|
|
|
|
for (device in devices) {
|
2020-07-15 15:14:37 +10:00
|
|
|
val endSessionMessage = OutgoingEndSessionMessage(OutgoingTextMessage(recipient, "TERMINATE", 0, -1))
|
|
|
|
MessageSender.send(context, endSessionMessage, threadID, false, null)
|
2020-05-14 11:19:20 +10:00
|
|
|
}
|
|
|
|
val infoMessage = OutgoingTextMessage(recipient, "", 0, 0)
|
|
|
|
val infoMessageID = smsDB.insertMessageOutbox(threadID, infoMessage, false, System.currentTimeMillis(), null)
|
|
|
|
if (infoMessageID > -1) {
|
|
|
|
smsDB.markAsSentLokiSessionRestorationRequest(infoMessageID)
|
|
|
|
}
|
|
|
|
lokiThreadDB.removeAllSessionRestoreDevices(threadID)
|
2020-05-14 10:44:13 +10:00
|
|
|
}
|
|
|
|
|
2020-05-11 16:54:31 +10:00
|
|
|
@JvmStatic
|
|
|
|
fun refreshSignedPreKey(context: Context) {
|
|
|
|
if (TextSecurePreferences.isSignedPreKeyRegistered(context)) {
|
|
|
|
Log.d("Loki", "Skipping signed pre key refresh; using existing signed pre key.")
|
|
|
|
} else {
|
|
|
|
Log.d("Loki", "Signed pre key refreshed successfully.")
|
|
|
|
val identityKeyPair = IdentityKeyUtil.getIdentityKeyPair(context)
|
|
|
|
PreKeyUtil.generateSignedPreKey(context, identityKeyPair, true)
|
|
|
|
TextSecurePreferences.setSignedPreKeyRegistered(context, true)
|
|
|
|
ApplicationContext.getInstance(context).jobManager.add(CleanPreKeysJob())
|
|
|
|
}
|
|
|
|
}
|
2020-05-13 10:24:20 +10:00
|
|
|
|
2020-07-16 09:44:00 +10:00
|
|
|
@JvmStatic
|
|
|
|
fun shouldProcessSessionRequest(context: Context, publicKey: String, timestamp: Long): Boolean {
|
|
|
|
val apiDB = DatabaseFactory.getLokiAPIDatabase(context)
|
|
|
|
val sentTimestamp = apiDB.getSessionRequestSentTimestamp(publicKey) ?: 0
|
|
|
|
val processedTimestamp = apiDB.getSessionRequestProcessedTimestamp(publicKey) ?: 0
|
|
|
|
return timestamp > sentTimestamp && timestamp > processedTimestamp
|
|
|
|
}
|
|
|
|
|
2020-05-13 10:24:20 +10:00
|
|
|
@JvmStatic
|
2020-05-13 12:29:31 +10:00
|
|
|
fun handlePreKeyBundleMessageIfNeeded(context: Context, content: SignalServiceContent) {
|
2020-07-15 15:14:37 +10:00
|
|
|
val preKeyBundleMessage = content.preKeyBundleMessage.orNull() ?: return
|
2020-07-16 09:44:00 +10:00
|
|
|
val publicKey = content.sender
|
|
|
|
if (recipient(context, publicKey).isGroupRecipient) { return } // Should never occur
|
2020-07-15 15:14:37 +10:00
|
|
|
Log.d("Loki", "Received a pre key bundle from: $publicKey.")
|
2020-07-16 09:44:00 +10:00
|
|
|
if (!shouldProcessSessionRequest(context, publicKey, content.timestamp)) {
|
|
|
|
Log.d("Loki", "Ignoring session request from: $publicKey.")
|
2020-07-15 15:14:37 +10:00
|
|
|
return
|
2020-05-22 10:41:31 +10:00
|
|
|
}
|
2020-07-16 09:44:00 +10:00
|
|
|
val registrationID = TextSecurePreferences.getLocalRegistrationId(context)
|
|
|
|
val lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context)
|
2020-07-15 15:14:37 +10:00
|
|
|
val preKeyBundle = preKeyBundleMessage.getPreKeyBundle(registrationID)
|
2020-07-16 09:44:00 +10:00
|
|
|
lokiPreKeyBundleDatabase.setPreKeyBundle(publicKey, preKeyBundle)
|
|
|
|
DatabaseFactory.getLokiAPIDatabase(context).setSessionRequestProcessedTimestamp(publicKey, Date().time)
|
|
|
|
val job = PushNullMessageSendJob(publicKey)
|
2020-07-15 15:14:37 +10:00
|
|
|
ApplicationContext.getInstance(context).jobManager.add(job)
|
2020-05-13 12:29:31 +10:00
|
|
|
}
|
|
|
|
|
|
|
|
@JvmStatic
|
2020-05-15 09:24:08 +10:00
|
|
|
fun handleEndSessionMessageIfNeeded(context: Context, content: SignalServiceContent) {
|
|
|
|
if (!content.dataMessage.isPresent || !content.dataMessage.get().isEndSession) { return }
|
2020-05-13 12:29:31 +10:00
|
|
|
val sessionStore = TextSecureSessionStore(context)
|
|
|
|
val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context)
|
|
|
|
Log.d("Loki", "Received a session reset request from: ${content.sender}; archiving the session.")
|
|
|
|
sessionStore.archiveAllSessions(content.sender)
|
2020-07-15 12:24:43 +10:00
|
|
|
lokiThreadDB.setSessionResetStatus(content.sender, SessionResetStatus.REQUEST_RECEIVED)
|
2020-05-13 12:29:31 +10:00
|
|
|
Log.d("Loki", "Sending an ephemeral message back to: ${content.sender}.")
|
2020-07-15 15:14:37 +10:00
|
|
|
val job = PushNullMessageSendJob(content.sender)
|
|
|
|
ApplicationContext.getInstance(context).jobManager.add(job)
|
2020-05-13 12:29:31 +10:00
|
|
|
SecurityEvent.broadcastSecurityUpdateEvent(context)
|
|
|
|
}
|
|
|
|
|
|
|
|
@JvmStatic
|
2020-05-14 13:52:20 +10:00
|
|
|
fun triggerSessionRestorationUI(context: Context, publicKey: String) {
|
|
|
|
val masterDevicePublicKey = MultiDeviceProtocol.shared.getMasterDevice(publicKey) ?: publicKey
|
|
|
|
val masterDeviceAsRecipient = recipient(context, masterDevicePublicKey)
|
|
|
|
if (masterDeviceAsRecipient.isGroupRecipient) { return }
|
|
|
|
val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(masterDeviceAsRecipient)
|
|
|
|
DatabaseFactory.getLokiThreadDatabase(context).addSessionRestoreDevice(threadID, publicKey)
|
2020-05-13 10:24:20 +10:00
|
|
|
}
|
2020-05-11 16:54:31 +10:00
|
|
|
}
|