From 90ee676a535d3f8dc64c433a1f2e1c9df4192375 Mon Sep 17 00:00:00 2001 From: Mikunj Date: Mon, 23 Sep 2019 15:17:19 +1000 Subject: [PATCH] Hookup receiving logic. --- .../securesms/jobs/PushDecryptJob.java | 119 +++++++++++++++--- .../securesms/util/TextSecurePreferences.java | 8 ++ 2 files changed, 109 insertions(+), 18 deletions(-) diff --git a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java index 1d9bf993aa..3a08ad14a2 100644 --- a/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java +++ b/src/org/thoughtcrime/securesms/jobs/PushDecryptJob.java @@ -41,21 +41,9 @@ import org.thoughtcrime.securesms.crypto.SecurityEvent; import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil; import org.thoughtcrime.securesms.crypto.storage.SignalProtocolStoreImpl; import org.thoughtcrime.securesms.crypto.storage.TextSecureSessionStore; -import org.thoughtcrime.securesms.database.Address; -import org.thoughtcrime.securesms.database.AttachmentDatabase; -import org.thoughtcrime.securesms.database.DatabaseFactory; -import org.thoughtcrime.securesms.database.GroupDatabase; -import org.thoughtcrime.securesms.database.GroupReceiptDatabase; -import org.thoughtcrime.securesms.database.MessagingDatabase; +import org.thoughtcrime.securesms.database.*; import org.thoughtcrime.securesms.database.MessagingDatabase.InsertResult; import org.thoughtcrime.securesms.database.MessagingDatabase.SyncMessageId; -import org.thoughtcrime.securesms.database.MmsDatabase; -import org.thoughtcrime.securesms.database.NoSuchMessageException; -import org.thoughtcrime.securesms.database.PushDatabase; -import org.thoughtcrime.securesms.database.RecipientDatabase; -import org.thoughtcrime.securesms.database.SmsDatabase; -import org.thoughtcrime.securesms.database.StickerDatabase; -import org.thoughtcrime.securesms.database.ThreadDatabase; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.StickerRecord; @@ -123,6 +111,8 @@ import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOper import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage; import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.push.SignalServiceAddress; +import org.whispersystems.signalservice.loki.api.LokiDeviceLinkingSession; +import org.whispersystems.signalservice.loki.api.LokiPairingAuthorisation; import org.whispersystems.signalservice.loki.crypto.LokiServiceCipher; import org.whispersystems.signalservice.loki.messaging.LokiMessageFriendRequestStatus; import org.whispersystems.signalservice.loki.messaging.LokiServiceMessage; @@ -295,14 +285,13 @@ public class PushDecryptJob extends BaseJob implements InjectableType { // Loki - Store the sender display name if needed Optional rawSenderDisplayName = content.senderDisplayName; if (rawSenderDisplayName.isPresent() && rawSenderDisplayName.get().length() > 0) { - String senderHexEncodedPublicKey = envelope.getSource(); - String senderDisplayName = rawSenderDisplayName.get() + " (..." + senderHexEncodedPublicKey.substring(senderHexEncodedPublicKey.length() - 8) + ")"; - DatabaseFactory.getLokiUserDatabase(context).setDisplayName(senderHexEncodedPublicKey, senderDisplayName); + setDisplayName(envelope.getSource(), rawSenderDisplayName.get()); } // TODO: Deleting the display name - - if (content.getDataMessage().isPresent()) { + if (content.getPairingAuthorisation().isPresent()) { + handlePairingAuthorisation(content.getPairingAuthorisation().get(), envelope, content); + } else if (content.getDataMessage().isPresent()) { SignalServiceDataMessage message = content.getDataMessage().get(); boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent(); @@ -1020,6 +1009,100 @@ public class PushDecryptJob extends BaseJob implements InjectableType { } } + private boolean isAuthorisationValid(@NonNull LokiPairingAuthorisation authorisation) { + boolean isSecondaryDevice = TextSecurePreferences.isSecondaryDevice(context); + String ourPubKey = TextSecurePreferences.getLocalNumber(context); + boolean isRequest = authorisation.getType() == LokiPairingAuthorisation.Type.REQUEST; + + if (authorisation.getRequestSignature() == null) { + Log.w("Loki", "Received a pairing request with missing request signature. Ignored."); + return false; + } else if (isRequest && isSecondaryDevice) { + Log.w("Loki", "Received a pairing request while being a secondary device. Ignored."); + return false; + } else if (isRequest && !authorisation.getPrimaryDevicePubKey().equals(ourPubKey)) { + Log.w("Loki", "Received a pairing request addressed to another pubkey. Ignored."); + return false; + } else if (isRequest && authorisation.getSecondaryDevicePubKey().equals(ourPubKey)) { + Log.w("Loki", "Received a pairing request from ourselves. Ignored."); + return false; + } + + return authorisation.verify(); + } + + private void handlePairingAuthorisation(@NonNull LokiPairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) { + if (authorisation.getType() == LokiPairingAuthorisation.Type.REQUEST) { + handlePairingRequest(authorisation, envelope); + } else if (authorisation.getSecondaryDevicePubKey().equals(TextSecurePreferences.getLocalNumber(context))) { + handlePairingAuthorisationForSelf(authorisation, envelope, content); + } else { + handlePairingAuthorisationForContact(authorisation); + } + } + + private void handlePairingRequest(@NonNull LokiPairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope) { + boolean valid = isAuthorisationValid(authorisation); + LokiDeviceLinkingSession linkingSession = LokiDeviceLinkingSession.Companion.getShared(); + if (valid && linkingSession.isListeningForLinkingRequest()) { + // Save to the database and trigger the event + DatabaseFactory.getLokiMultiDeviceDatabase(context).insertOrUpdatePairingAuthorisation(authorisation); + linkingSession.receivedLinkingRequest(authorisation); + } else { + // Remove pre key bundle from the user + DatabaseFactory.getLokiPreKeyBundleDatabase(context).removePreKeyBundle(envelope.getSource()); + } + } + + private void handlePairingAuthorisationForSelf(@NonNull LokiPairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) { + if (TextSecurePreferences.isSecondaryDevice(context)) { + Log.w("Loki", "Received an unexpected pairing authorisation (device is already paired as secondary device). Ignoring."); + return; + } + + if (!isAuthorisationValid(authorisation)) { + Log.w("Loki", "Received invalid pairing authorisation for self. Could not verify signature. Ignoring."); + return; + } + + // Unimplemented + if (authorisation.getType() != LokiPairingAuthorisation.Type.GRANT) { return; } + + // Set the current device as secondary + Log.d("Loki", "Receiving pairing authorisation from: " + authorisation.getPrimaryDevicePubKey()); + DatabaseFactory.getLokiMultiDeviceDatabase(context).insertOrUpdatePairingAuthorisation(authorisation); + TextSecurePreferences.setIsSecondaryDevice(context, true); + // TODO: Trigger an event here? + + // Update display names + if (content.senderDisplayName.isPresent() && content.senderDisplayName.get().length() > 0) { + setDisplayName(envelope.getSource(), content.senderDisplayName.get()); + } + } + + private void handlePairingAuthorisationForContact(@NonNull LokiPairingAuthorisation authorisation) { + if (!isAuthorisationValid(authorisation)) { + Log.w("Loki", "Received invalid pairing authorisation for self. Could not verify signature. Ignoring."); + return; + } + + // Ensure primary device is a friend + String primaryDevice = authorisation.getPrimaryDevicePubKey(); + LokiThreadDatabase lokiThreadDatabase = DatabaseFactory.getLokiThreadDatabase(context); + long threadID = DatabaseFactory.getThreadDatabase(context).getThreadIdIfExistsFor(Recipient.from(context, Address.fromSerialized(primaryDevice), false)); + LokiThreadFriendRequestStatus threadFriendRequestStatus = lokiThreadDatabase.getFriendRequestStatus(threadID); + if (threadFriendRequestStatus == LokiThreadFriendRequestStatus.FRIENDS) { + // If we're friends then save and send a background message if needed for friend request accept + DatabaseFactory.getLokiMultiDeviceDatabase(context).insertOrUpdatePairingAuthorisation(authorisation); + sendBackgroundMessage(authorisation.getSecondaryDevicePubKey()); + } + } + + private void setDisplayName(String pubKey, String profileName) { + String senderDisplayName = profileName + " (..." + pubKey.substring(pubKey.length() - 8) + ")"; + DatabaseFactory.getLokiUserDatabase(context).setDisplayName(pubKey, senderDisplayName); + } + private void updateGroupChatMessageServerID(Optional messageServerIDOrNull, Optional insertResult) { if (insertResult.isPresent() && messageServerIDOrNull.isPresent()) { long messageID = insertResult.get().getMessageId(); diff --git a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java index f251a9824a..7dff371799 100644 --- a/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java +++ b/src/org/thoughtcrime/securesms/util/TextSecurePreferences.java @@ -1169,5 +1169,13 @@ public class TextSecurePreferences { public static void markChatSetUp(Context context, String id) { setBooleanPreference(context, "is_chat_set_up" + "?chat=" + id, true); } + + public static boolean isSecondaryDevice(Context context) { + return getBooleanPreference(context, "is_secondary_device", false); + } + + public static void setIsSecondaryDevice(Context context, boolean isSecondaryDevice) { + setBooleanPreference(context, "is_secondary_device", isSecondaryDevice); + } // endregion }