From a7b94d188fafcef5449dee691891a727bb9fe816 Mon Sep 17 00:00:00 2001 From: nielsandriesse Date: Fri, 22 May 2020 13:41:36 +1000 Subject: [PATCH] Fix various session reset issues --- .../securesms/ApplicationContext.java | 2 +- .../securesms/jobs/PushDecryptJob.java | 12 +++++++----- .../jobs/PushNotificationReceiveJob.java | 2 +- .../securesms/jobs/PushReceivedJob.java | 8 ++++---- .../securesms/loki/api/BackgroundPollWorker.kt | 2 +- .../loki/api/PushNotificationService.kt | 2 +- .../loki/protocol/MultiDeviceProtocol.kt | 18 ++++++++---------- .../loki/protocol/SessionManagementProtocol.kt | 1 - .../service/IncomingMessageObserver.java | 12 ++++++------ .../securesms/sms/MessageSender.java | 12 ++++++------ 10 files changed, 35 insertions(+), 36 deletions(-) diff --git a/src/org/thoughtcrime/securesms/ApplicationContext.java b/src/org/thoughtcrime/securesms/ApplicationContext.java index 3d533ddb72..8fde91e378 100644 --- a/src/org/thoughtcrime/securesms/ApplicationContext.java +++ b/src/org/thoughtcrime/securesms/ApplicationContext.java @@ -490,7 +490,7 @@ public class ApplicationContext extends MultiDexApplication implements Dependenc LokiAPI.Companion.configureIfNeeded(userPublicKey, apiDB, broadcaster); lokiPoller = new LokiPoller(userPublicKey, apiDB, protos -> { for (SignalServiceProtos.Envelope proto : protos) { - new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto)); + new PushContentReceiveJob(context).processEnvelope(new SignalServiceEnvelope(proto), false); } return Unit.INSTANCE; }); diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 43c0a42bc7..0206e6653c 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -207,7 +207,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { SignalServiceEnvelope envelope = database.get(messageId); Optional optionalSmsMessageId = smsMessageId > 0 ? Optional.of(smsMessageId) : Optional.absent(); - handleMessage(envelope, optionalSmsMessageId); + handleMessage(envelope, optionalSmsMessageId, false); database.delete(messageId); } } @@ -222,7 +222,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } - public void processMessage(@NonNull SignalServiceEnvelope envelope) { + public void processMessage(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) { synchronized (PushReceivedJob.RECEIVE_LOCK) { if (needsMigration()) { Log.w(TAG, "Skipping and storing envelope, waiting for migration..."); @@ -231,7 +231,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { return; } - handleMessage(envelope, Optional.absent()); + handleMessage(envelope, Optional.absent(), isPushNotification); } } @@ -253,7 +253,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } - private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional smsMessageId) { + private void handleMessage(@NonNull SignalServiceEnvelope envelope, @NonNull Optional smsMessageId, boolean isPushNotification) { try { GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); @@ -378,7 +378,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType { handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); } catch (ProtocolInvalidMessageException e) { Log.w(TAG, e); - handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); + if (!isPushNotification) { // This can be triggered if a PN encrypted with an old session comes in after the user performed a session reset + handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); + } } catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolUntrustedIdentityException e) { Log.w(TAG, e); handleCorruptMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); diff --git a/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java b/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java index dcc5c706c0..3d38c1c833 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushNotificationReceiveJob.java @@ -57,7 +57,7 @@ public class PushNotificationReceiveJob extends PushReceivedJob implements Injec synchronized (PushReceivedJob.RECEIVE_LOCK) { receiver.retrieveMessages(envelope -> { Log.i(tag, "Retrieved an envelope." + timeSuffix(startTime)); - processEnvelope(envelope); + processEnvelope(envelope, false); Log.i(tag, "Successfully processed an envelope." + timeSuffix(startTime)); }); TextSecurePreferences.setNeedsMessagePull(context, false); diff --git a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java index 422ce6b6d7..c93ee94cd0 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushReceivedJob.java @@ -22,7 +22,7 @@ public abstract class PushReceivedJob extends BaseJob { super(parameters); } - public void processEnvelope(@NonNull SignalServiceEnvelope envelope) { + public void processEnvelope(@NonNull SignalServiceEnvelope envelope, boolean isPushNotification) { synchronized (RECEIVE_LOCK) { try { if (envelope.hasSource()) { @@ -37,7 +37,7 @@ public abstract class PushReceivedJob extends BaseJob { if (envelope.isReceipt()) { handleReceipt(envelope); } else if (envelope.isPreKeySignalMessage() || envelope.isSignalMessage() || envelope.isUnidentifiedSender() || envelope.isFriendRequest()) { - handleMessage(envelope); + handleMessage(envelope, isPushNotification); } else { Log.w(TAG, "Received envelope of unknown type: " + envelope.getType()); } @@ -47,8 +47,8 @@ public abstract class PushReceivedJob extends BaseJob { } } - private void handleMessage(SignalServiceEnvelope envelope) { - new PushDecryptJob(context).processMessage(envelope); + private void handleMessage(SignalServiceEnvelope envelope, boolean isPushNotification) { + new PushDecryptJob(context).processMessage(envelope, isPushNotification); } @SuppressLint("DefaultLocale") diff --git a/src/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt b/src/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt index 1555d65665..2d64ec1489 100644 --- a/src/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt +++ b/src/org/thoughtcrime/securesms/loki/api/BackgroundPollWorker.kt @@ -38,7 +38,7 @@ class BackgroundPollWorker : PersistentAlarmManagerListener() { LokiAPI.configureIfNeeded(userHexEncodedPublicKey, lokiAPIDatabase, broadcaster) LokiAPI.shared.getMessages().map { messages -> messages.forEach { - PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it)) + PushContentReceiveJob(context).processEnvelope(SignalServiceEnvelope(it), false) } } } catch (exception: Throwable) { diff --git a/src/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt b/src/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt index 867f4e2196..bdc55d10dc 100644 --- a/src/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt +++ b/src/org/thoughtcrime/securesms/loki/api/PushNotificationService.kt @@ -24,7 +24,7 @@ class PushNotificationService : FirebaseMessagingService() { if (data != null) { try { val envelope = LokiMessageWrapper.unwrap(data) - PushContentReceiveJob(this).processEnvelope(SignalServiceEnvelope(envelope)) + PushContentReceiveJob(this).processEnvelope(SignalServiceEnvelope(envelope), true) } catch (e: Exception) { Log.d("Loki", "Failed to unwrap data for message.") } diff --git a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt index d2fc5fa2be..6e6d30b96b 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/MultiDeviceProtocol.kt @@ -2,7 +2,6 @@ package org.thoughtcrime.securesms.loki.protocol import android.content.Context import android.util.Log -import network.loki.messenger.BuildConfig import nl.komponents.kovenant.Promise import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.crypto.IdentityKeyUtil @@ -34,21 +33,21 @@ object MultiDeviceProtocol { enum class MessageType { Text, Media } @JvmStatic - fun sendTextPush(context: Context, recipient: Recipient, messageID: Long) { - sendMessagePush(context, recipient, messageID, MessageType.Text) + fun sendTextPush(context: Context, recipient: Recipient, messageID: Long, isEndSession: Boolean) { + sendMessagePush(context, recipient, messageID, MessageType.Text, isEndSession) } @JvmStatic fun sendMediaPush(context: Context, recipient: Recipient, messageID: Long) { - sendMessagePush(context, recipient, messageID, MessageType.Media) + sendMessagePush(context, recipient, messageID, MessageType.Media, false) } - private fun sendMessagePushToDevice(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType): PushSendJob { + private fun sendMessagePushToDevice(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType, isEndSession: Boolean): PushSendJob { val threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient) val threadFRStatus = DatabaseFactory.getLokiThreadDatabase(context).getFriendRequestStatus(threadID) val isNoteToSelf = SessionMetaProtocol.shared.isNoteToSelf(recipient.address.serialize()) val isContactFriend = (threadFRStatus == LokiThreadFriendRequestStatus.FRIENDS || isNoteToSelf) // In the note to self case the device linking request was the FR - val isFRMessage = !isContactFriend // Holds true assuming this method isn't invoked for control messages + val isFRMessage = !isContactFriend val hasVisibleContent = when (messageType) { MessageType.Text -> DatabaseFactory.getSmsDatabase(context).getMessage(messageID).body.isNotBlank() MessageType.Media -> { @@ -56,10 +55,9 @@ object MultiDeviceProtocol { outgoingMediaMessage.body.isNotBlank() || outgoingMediaMessage.attachments.isNotEmpty() } } - if (isFRMessage && !hasVisibleContent && BuildConfig.DEBUG) { throw IllegalStateException() } // Verify the above assumption val shouldSendAutoGeneratedFR = !isContactFriend && !isFRMessage && !isNoteToSelf && !recipient.address.isGroup // Group threads work through session requests - && hasVisibleContent + && hasVisibleContent && !isEndSession if (!shouldSendAutoGeneratedFR) { when (messageType) { MessageType.Text -> return PushTextSendJob(messageID, messageID, recipient.address, isFRMessage, null) @@ -74,7 +72,7 @@ object MultiDeviceProtocol { } } - private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType) { + private fun sendMessagePush(context: Context, recipient: Recipient, messageID: Long, messageType: MessageType, isEndSession: Boolean) { val jobManager = ApplicationContext.getInstance(context).jobManager val isMultiDeviceRequired = !recipient.address.isOpenGroup if (!isMultiDeviceRequired) { @@ -86,7 +84,7 @@ object MultiDeviceProtocol { val publicKey = recipient.address.serialize() LokiFileServerAPI.shared.getDeviceLinks(publicKey).success { val devices = MultiDeviceProtocol.shared.getAllLinkedDevices(publicKey) - val jobs = devices.map { sendMessagePushToDevice(context, recipient(context, it), messageID, messageType) } + val jobs = devices.map { sendMessagePushToDevice(context, recipient(context, it), messageID, messageType, isEndSession) } @Suppress("UNCHECKED_CAST") when (messageType) { MessageType.Text -> jobManager.startChain(jobs).enqueue() diff --git a/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt b/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt index 61a1679883..b29808a426 100644 --- a/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt +++ b/src/org/thoughtcrime/securesms/loki/protocol/SessionManagementProtocol.kt @@ -89,7 +89,6 @@ object SessionManagementProtocol { @JvmStatic fun handleEndSessionMessageIfNeeded(context: Context, content: SignalServiceContent) { if (!content.dataMessage.isPresent || !content.dataMessage.get().isEndSession) { return } - // TODO: Notify the user val sessionStore = TextSecureSessionStore(context) val lokiThreadDB = DatabaseFactory.getLokiThreadDatabase(context) Log.d("Loki", "Received a session reset request from: ${content.sender}; archiving the session.") diff --git a/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java b/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java index 1a87309cfe..cfc9215cee 100644 --- a/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java +++ b/src/org/thoughtcrime/securesms/service/IncomingMessageObserver.java @@ -12,15 +12,13 @@ import android.support.annotation.Nullable; import android.support.v4.app.NotificationCompat; import android.support.v4.content.ContextCompat; +import org.thoughtcrime.securesms.ApplicationContext; +import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobmanager.ConstraintObserver; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint; import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraintObserver; -import org.thoughtcrime.securesms.logging.Log; - -import org.thoughtcrime.securesms.ApplicationContext; -import network.loki.messenger.R; -import org.thoughtcrime.securesms.dependencies.InjectableType; import org.thoughtcrime.securesms.jobs.PushContentReceiveJob; +import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.notifications.NotificationChannels; import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess; import org.thoughtcrime.securesms.util.TextSecurePreferences; @@ -33,6 +31,8 @@ import java.util.concurrent.TimeoutException; import javax.inject.Inject; +import network.loki.messenger.R; + public class IncomingMessageObserver implements InjectableType, ConstraintObserver.Notifier { private static final String TAG = IncomingMessageObserver.class.getSimpleName(); @@ -159,7 +159,7 @@ public class IncomingMessageObserver implements InjectableType, ConstraintObserv localPipe.read(REQUEST_TIMEOUT_MINUTES, TimeUnit.MINUTES, envelope -> { Log.i(TAG, "Retrieved envelope! " + String.valueOf(envelope.getSource())); - new PushContentReceiveJob(context).processEnvelope(envelope); + new PushContentReceiveJob(context).processEnvelope(envelope, false); }); } catch (TimeoutException e) { Log.w(TAG, "Application level read timeout..."); diff --git a/src/org/thoughtcrime/securesms/sms/MessageSender.java b/src/org/thoughtcrime/securesms/sms/MessageSender.java index 36a3912a2a..35f48d702c 100644 --- a/src/org/thoughtcrime/securesms/sms/MessageSender.java +++ b/src/org/thoughtcrime/securesms/sms/MessageSender.java @@ -81,7 +81,7 @@ public class MessageSender { FriendRequestProtocol.setFriendRequestStatusToSendingIfNeeded(context, messageId, allocatedThreadId); } - sendTextMessage(context, recipient, forceSms, keyExchange, messageId); + sendTextMessage(context, recipient, forceSms, keyExchange, messageId, message.isEndSession()); return allocatedThreadId; } @@ -135,7 +135,7 @@ public class MessageSender { if (messageRecord.isMms()) { sendMediaMessage(context, recipient, forceSms, messageId, expiresIn); } else { - sendTextMessage(context, recipient, forceSms, keyExchange, messageId); + sendTextMessage(context, recipient, forceSms, keyExchange, messageId, messageRecord.isEndSession()); } } @@ -152,17 +152,17 @@ public class MessageSender { private static void sendTextMessage(Context context, Recipient recipient, boolean forceSms, boolean keyExchange, - long messageId) + long messageId, boolean isEndSession) { if (isLocalSelfSend(context, recipient, forceSms)) { sendLocalTextSelf(context, messageId); } else { - sendTextPush(context, recipient, messageId); + sendTextPush(context, recipient, messageId, isEndSession); } } - private static void sendTextPush(Context context, Recipient recipient, long messageId) { - MultiDeviceProtocol.sendTextPush(context, recipient, messageId); + private static void sendTextPush(Context context, Recipient recipient, long messageId, boolean isEndSession) { + MultiDeviceProtocol.sendTextPush(context, recipient, messageId, isEndSession); } private static void sendMediaPush(Context context, Recipient recipient, long messageId) {