diff --git a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java index 38237484f5..0e67b89b42 100644 --- a/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java +++ b/src/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java @@ -10,11 +10,11 @@ import org.thoughtcrime.securesms.logging.Log; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.protocol.CiphertextMessage; import org.whispersystems.libsignal.state.SessionRecord; -import org.whispersystems.signalservice.loki.messaging.LokiSessionDatabaseProtocol; +import org.whispersystems.libsignal.state.SessionStore; import java.util.List; -public class TextSecureSessionStore implements LokiSessionDatabaseProtocol { +public class TextSecureSessionStore implements SessionStore { private static final String TAG = TextSecureSessionStore.class.getSimpleName(); diff --git a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java index b57185dd24..c59224d481 100644 --- a/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java +++ b/src/org/thoughtcrime/securesms/dependencies/SignalCommunicationModule.java @@ -7,7 +7,6 @@ import org.thoughtcrime.securesms.ApplicationContext; import org.thoughtcrime.securesms.CreateProfileActivity; import org.thoughtcrime.securesms.DeviceListFragment; import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; -import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.events.ReminderUpdateEvent; import org.thoughtcrime.securesms.gcm.FcmService; @@ -48,6 +47,7 @@ import org.thoughtcrime.securesms.jobs.StickerPackDownloadJob; import org.thoughtcrime.securesms.jobs.TypingSendJob; import org.thoughtcrime.securesms.linkpreview.LinkPreviewRepository; import org.thoughtcrime.securesms.logging.Log; +import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.PushMessageSyncSendJob; import org.thoughtcrime.securesms.preferences.AppProtectionPreferenceFragment; import org.thoughtcrime.securesms.push.MessageSenderEventListener; @@ -160,7 +160,7 @@ public class SignalCommunicationModule { DatabaseFactory.getLokiThreadDatabase(context), DatabaseFactory.getLokiMessageDatabase(context), DatabaseFactory.getLokiPreKeyBundleDatabase(context), - new TextSecureSessionStore(context), + new LokiSessionResetImplementation(context), DatabaseFactory.getLokiUserDatabase(context), ((ApplicationContext)context.getApplicationContext()).broadcaster); } else { diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 4f382ae225..afb86f70b6 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -69,12 +69,12 @@ import org.thoughtcrime.securesms.linkpreview.LinkPreviewUtil; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.loki.FriendRequestHandler; import org.thoughtcrime.securesms.loki.LokiMessageDatabase; +import org.thoughtcrime.securesms.loki.LokiSessionResetImplementation; import org.thoughtcrime.securesms.loki.LokiThreadDatabase; import org.thoughtcrime.securesms.loki.MultiDeviceUtilities; import org.thoughtcrime.securesms.loki.redesign.activities.HomeActivity; import org.thoughtcrime.securesms.loki.redesign.messaging.LokiAPIUtilities; import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyBundleDatabase; -import org.thoughtcrime.securesms.loki.redesign.messaging.LokiPreKeyRecordDatabase; import org.thoughtcrime.securesms.mms.IncomingMediaMessage; import org.thoughtcrime.securesms.mms.MmsException; import org.thoughtcrime.securesms.mms.OutgoingExpirationUpdateMessage; @@ -101,6 +101,8 @@ import org.thoughtcrime.securesms.util.IdentityUtil; import org.thoughtcrime.securesms.util.MediaUtil; import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.Util; +import org.whispersystems.libsignal.loki.LokiSessionResetProtocol; +import org.whispersystems.libsignal.loki.LokiSessionResetStatus; import org.whispersystems.libsignal.state.PreKeyBundle; import org.whispersystems.libsignal.state.SignalProtocolStore; import org.whispersystems.libsignal.util.guava.Optional; @@ -137,7 +139,6 @@ import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage; import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus; -import org.whispersystems.signalservice.loki.messaging.LokiThreadSessionResetStatus; import org.whispersystems.signalservice.loki.utilities.PromiseUtil; import java.io.InputStream; @@ -270,9 +271,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType { GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); SignalProtocolStore axolotlStore = new SignalProtocolStoreImpl(context); LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context); - LokiPreKeyRecordDatabase lokiPreKeyRecordDatabase = DatabaseFactory.getLokiPreKeyRecordDatabase(context); + LokiSessionResetProtocol lokiSessionResetProtocol = new LokiSessionResetImplementation(context); SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context)); - LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiThreadDatabase, lokiPreKeyRecordDatabase, UnidentifiedAccessUtil.getCertificateValidator()); + LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiSessionResetProtocol, UnidentifiedAccessUtil.getCertificateValidator()); SignalServiceContent content = cipher.decrypt(envelope); @@ -363,6 +364,18 @@ public class PushDecryptJob extends BaseJob implements InjectableType { handleNeedsDeliveryReceipt(content, message); } + // If we received a friend request, but we were already friends with the user, reset the session + if (content.isFriendRequest() && !message.isGroupMessage()) { + Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false); + ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); + long threadID = threadDatabase.getThreadIdIfExistsFor(sender); + if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) { + resetSession(content.getSender()); + // Let our other devices know that we have reset the session + MessageSender.syncContact(context, sender.getAddress()); + } + } + // Loki - Handle friend request logic if needed updateFriendRequestStatusIfNeeded(content, message); } @@ -403,11 +416,6 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (envelope.isPreKeySignalMessage()) { ApplicationContext.getInstance(context).getJobManager().add(new RefreshPreKeysJob()); } - - // Loki - Handle session reset logic - if (!content.isFriendRequest()) { - cipher.handleSessionResetRequestIfNeeded(content, cipher.getSessionStatus(content)); - } } catch (ProtocolInvalidVersionException e) { Log.w(TAG, e); handleInvalidVersionMessage(e.getSender(), e.getSenderDevice(), envelope.getTimestamp(), smsMessageId); @@ -540,19 +548,19 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } if (threadId != null) { - resetSession(content.getSender(), threadId); + resetSession(content.getSender()); MessageNotifier.updateNotification(context, threadId); } } - private void resetSession(String hexEncodedPublicKey, long threadId) { + private void resetSession(String hexEncodedPublicKey) { TextSecureSessionStore sessionStore = new TextSecureSessionStore(context); LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context); Log.d("Loki", "Received a session reset request from: " + hexEncodedPublicKey + "; archiving the session."); sessionStore.archiveAllSessions(hexEncodedPublicKey); - lokiThreadDatabase.setSessionResetStatus(threadId, LokiThreadSessionResetStatus.REQUEST_RECEIVED); + lokiThreadDatabase.setSessionResetStatus(hexEncodedPublicKey, LokiSessionResetStatus.REQUEST_RECEIVED); Log.d("Loki", "Sending a ping back to " + hexEncodedPublicKey + "."); MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey); @@ -1203,21 +1211,11 @@ public class PushDecryptJob extends BaseJob implements InjectableType { if (lokiMessage.getPreKeyBundleMessage() == null) { return; } int registrationID = TextSecurePreferences.getLocalRegistrationId(context); LokiPreKeyBundleDatabase lokiPreKeyBundleDatabase = DatabaseFactory.getLokiPreKeyBundleDatabase(context); - ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); - LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context); if (registrationID <= 0) { return; } Log.d("Loki", "Received a pre key bundle from: " + content.getSender() + "."); PreKeyBundle preKeyBundle = lokiMessage.getPreKeyBundleMessage().getPreKeyBundle(registrationID); lokiPreKeyBundleDatabase.setPreKeyBundle(content.getSender(), preKeyBundle); - // If we received a friend request, but we were already friends with the user, reset the session - if (content.isFriendRequest()) { - long threadID = threadDatabase.getThreadIdIfExistsFor(sender); - if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) { - resetSession(content.getSender(), threadID); - // Let our other devices know that we have reset the session - MessageSender.syncContact(context, sender.getAddress()); - } - } + } private void handleSessionRequestIfNeeded(@NonNull SignalServiceContent content) { diff --git a/src/org/thoughtcrime/securesms/loki/LokiSessionResetImplementation.kt b/src/org/thoughtcrime/securesms/loki/LokiSessionResetImplementation.kt new file mode 100644 index 0000000000..f2f92edfd7 --- /dev/null +++ b/src/org/thoughtcrime/securesms/loki/LokiSessionResetImplementation.kt @@ -0,0 +1,34 @@ +package org.thoughtcrime.securesms.loki + +import android.content.Context +import org.thoughtcrime.securesms.database.DatabaseFactory +import org.thoughtcrime.securesms.sms.MessageSender +import org.whispersystems.libsignal.loki.LokiSessionResetProtocol +import org.whispersystems.libsignal.loki.LokiSessionResetStatus +import org.whispersystems.libsignal.protocol.PreKeySignalMessage + +class LokiSessionResetImplementation(private val context: Context) : LokiSessionResetProtocol { + + override fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus { + return DatabaseFactory.getLokiThreadDatabase(context).getSessionResetStatus(hexEncodedPublicKey) + } + + override fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) { + return DatabaseFactory.getLokiThreadDatabase(context).setSessionResetStatus(hexEncodedPublicKey, sessionResetStatus) + } + + override fun onNewSessionAdopted(hexEncodedPublicKey: String, oldSessionResetStatus: LokiSessionResetStatus) { + if (oldSessionResetStatus == LokiSessionResetStatus.IN_PROGRESS) { + // Send a message back to the contact to finalise session reset + MessageSender.sendBackgroundMessage(context, hexEncodedPublicKey) + } + + // TODO: Show session reset succeed message + } + + override fun validatePreKeySignalMessage(sender: String, message: PreKeySignalMessage) { + val preKeyRecord = DatabaseFactory.getLokiPreKeyRecordDatabase(context).getPreKeyRecord(sender) + check(preKeyRecord != null) { "Received a background message from a user without an associated pre key record." } + check(preKeyRecord.id == (message.preKeyId ?: -1)) { "Received a background message from an unknown source." } + } +} \ No newline at end of file diff --git a/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt b/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt index b6aefdaa3a..0decbdd0c5 100644 --- a/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt +++ b/src/org/thoughtcrime/securesms/loki/LokiThreadDatabase.kt @@ -10,11 +10,11 @@ import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper import org.thoughtcrime.securesms.loki.redesign.utilities.* import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.TextSecurePreferences +import org.whispersystems.libsignal.loki.LokiSessionResetStatus import org.whispersystems.signalservice.internal.util.JsonUtil import org.whispersystems.signalservice.loki.api.LokiPublicChat import org.whispersystems.signalservice.loki.messaging.LokiThreadDatabaseProtocol import org.whispersystems.signalservice.loki.messaging.LokiThreadFriendRequestStatus -import org.whispersystems.signalservice.loki.messaging.LokiThreadSessionResetStatus import org.whispersystems.signalservice.loki.utilities.PublicKeyValidation class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(context, helper), LokiThreadDatabaseProtocol { @@ -23,11 +23,11 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa companion object { private val friendRequestTableName = "loki_thread_friend_request_database" private val sessionResetTableName = "loki_thread_session_reset_database" - public val publicChatTableName = "loki_public_chat_database" - public val threadID = "thread_id" + val publicChatTableName = "loki_public_chat_database" + val threadID = "thread_id" private val friendRequestStatus = "friend_request_status" private val sessionResetStatus = "session_reset_status" - public val publicChat = "public_chat" + val publicChat = "public_chat" @JvmStatic val createFriendRequestTableCommand = "CREATE TABLE $friendRequestTableName ($threadID INTEGER PRIMARY KEY, $friendRequestStatus INTEGER DEFAULT 0);" @JvmStatic val createSessionResetTableCommand = "CREATE TABLE $sessionResetTableName ($threadID INTEGER PRIMARY KEY, $sessionResetStatus INTEGER DEFAULT 0);" @JvmStatic val createPublicChatTableCommand = "CREATE TABLE $publicChatTableName ($threadID INTEGER PRIMARY KEY, $publicChat TEXT);" @@ -79,19 +79,21 @@ class LokiThreadDatabase(context: Context, helper: SQLCipherOpenHelper) : Databa || friendRequestStatus == LokiThreadFriendRequestStatus.REQUEST_RECEIVED } - override fun getSessionResetStatus(threadID: Long): LokiThreadSessionResetStatus { + fun getSessionResetStatus(hexEncodedPublicKey: String): LokiSessionResetStatus { + val threadID = getThreadID(hexEncodedPublicKey) val database = databaseHelper.readableDatabase val result = database.get(sessionResetTableName, "${Companion.threadID} = ?", arrayOf( threadID.toString() )) { cursor -> cursor.getInt(sessionResetStatus) } return if (result != null) { - LokiThreadSessionResetStatus.values().first { it.rawValue == result } + LokiSessionResetStatus.values().first { it.rawValue == result } } else { - LokiThreadSessionResetStatus.NONE + LokiSessionResetStatus.NONE } } - override fun setSessionResetStatus(threadID: Long, sessionResetStatus: LokiThreadSessionResetStatus) { + fun setSessionResetStatus(hexEncodedPublicKey: String, sessionResetStatus: LokiSessionResetStatus) { + val threadID = getThreadID(hexEncodedPublicKey) val database = databaseHelper.writableDatabase val contentValues = ContentValues(2) contentValues.put(Companion.threadID, threadID)