Stop relying on SignalServiceEnvelope and instead use SignalServiceContent to determine message sender and friend request

This commit is contained in:
Mikunj 2020-01-29 13:49:39 +11:00
parent 01f5ff7c86
commit 62d391085b
6 changed files with 35 additions and 38 deletions

View File

@ -30,12 +30,7 @@ public class UnidentifiedAccessUtil {
private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName(); private static final String TAG = UnidentifiedAccessUtil.class.getSimpleName();
public static CertificateValidator getCertificateValidator() { public static CertificateValidator getCertificateValidator() {
try { return new CertificateValidator();
ECPublicKey unidentifiedSenderTrustRoot = Curve.decodePoint(Base64.decode(BuildConfig.UNIDENTIFIED_SENDER_TRUST_ROOT), 0);
return new CertificateValidator(unidentifiedSenderTrustRoot);
} catch (InvalidKeyException | IOException e) {
throw new AssertionError(e);
}
} }
@WorkerThread @WorkerThread

View File

@ -274,18 +274,14 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context)); SignalServiceAddress localAddress = new SignalServiceAddress(TextSecurePreferences.getLocalNumber(context));
LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiThreadDatabase, lokiPreKeyRecordDatabase, UnidentifiedAccessUtil.getCertificateValidator()); LokiServiceCipher cipher = new LokiServiceCipher(localAddress, axolotlStore, lokiThreadDatabase, lokiPreKeyRecordDatabase, UnidentifiedAccessUtil.getCertificateValidator());
// Loki - Handle session reset logic SignalServiceContent content = cipher.decrypt(envelope);
if (!envelope.isFriendRequest() && cipher.getSessionStatus(envelope) == null && envelope.isPreKeySignalMessage()) {
cipher.validateBackgroundMessage(envelope, envelope.getContent());
}
// Loki - Ignore any friend requests that we got before restoration // Loki - Ignore any friend requests that we got before restoration
if (envelope.isFriendRequest() && envelope.getTimestamp() < TextSecurePreferences.getRestorationTime(context)) { if (content.isFriendRequest() && content.getTimestamp() < TextSecurePreferences.getRestorationTime(context)) {
Log.d("Loki", "Ignoring friend request received before restoration."); Log.d("Loki", "Ignoring friend request received before restoration.");
return; return;
} }
SignalServiceContent content = cipher.decrypt(envelope);
if (shouldIgnore(content)) { if (shouldIgnore(content)) {
Log.i(TAG, "Ignoring message."); Log.i(TAG, "Ignoring message.");
@ -293,12 +289,12 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
// Loki - Handle friend request acceptance if needed // Loki - Handle friend request acceptance if needed
acceptFriendRequestIfNeeded(envelope, content); acceptFriendRequestIfNeeded(content);
// Loki - Store pre key bundle // Loki - Store pre key bundle
// We shouldn't store it if it's a pairing message // We shouldn't store it if it's a pairing message
if (!content.getPairingAuthorisation().isPresent()) { if (!content.getPairingAuthorisation().isPresent()) {
storePreKeyBundleIfNeeded(envelope, content); storePreKeyBundleIfNeeded(content);
} }
if (content.lokiServiceMessage.isPresent()) { if (content.lokiServiceMessage.isPresent()) {
@ -313,24 +309,24 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (rawSenderDisplayName.isPresent() && rawSenderDisplayName.get().length() > 0) { if (rawSenderDisplayName.isPresent() && rawSenderDisplayName.get().length() > 0) {
// If we got a name from our primary device then we set our profile name to match it // If we got a name from our primary device then we set our profile name to match it
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
if (ourPrimaryDevice != null && envelope.getSource().equals(ourPrimaryDevice)) { if (ourPrimaryDevice != null && content.getSender().equals(ourPrimaryDevice)) {
TextSecurePreferences.setProfileName(context, rawSenderDisplayName.get()); TextSecurePreferences.setProfileName(context, rawSenderDisplayName.get());
} }
// If we receive a message from our device then don't set the display name in the database (as we probably have a alias set for them) // If we receive a message from our device then don't set the display name in the database (as we probably have a alias set for them)
MultiDeviceUtilities.isOneOfOurDevices(context, Address.fromSerialized(content.getSender())).success(isOneOfOurDevice -> { MultiDeviceUtilities.isOneOfOurDevices(context, Address.fromSerialized(content.getSender())).success(isOneOfOurDevice -> {
if (!isOneOfOurDevice) { setDisplayName(envelope.getSource(), rawSenderDisplayName.get()); } if (!isOneOfOurDevice) { setDisplayName(content.getSender(), rawSenderDisplayName.get()); }
return Unit.INSTANCE; return Unit.INSTANCE;
}); });
} }
if (content.getPairingAuthorisation().isPresent()) { if (content.getPairingAuthorisation().isPresent()) {
handlePairingMessage(content.getPairingAuthorisation().get(), envelope, content); handlePairingMessage(content.getPairingAuthorisation().get(), content);
} else if (content.getDataMessage().isPresent()) { } else if (content.getDataMessage().isPresent()) {
SignalServiceDataMessage message = content.getDataMessage().get(); SignalServiceDataMessage message = content.getDataMessage().get();
boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent(); boolean isMediaMessage = message.getAttachments().isPresent() || message.getQuote().isPresent() || message.getSharedContacts().isPresent() || message.getPreviews().isPresent() || message.getSticker().isPresent();
if (!envelope.isFriendRequest() && message.isUnpairingRequest()) { if (!content.isFriendRequest() && message.isUnpairingRequest()) {
// Make sure we got the request from our primary device // Make sure we got the request from our primary device
String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context); String ourPrimaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context);
if (ourPrimaryDevice != null && ourPrimaryDevice.equals(content.getSender())) { if (ourPrimaryDevice != null && ourPrimaryDevice.equals(content.getSender())) {
@ -363,7 +359,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
// Loki - Handle friend request logic if needed // Loki - Handle friend request logic if needed
updateFriendRequestStatusIfNeeded(envelope, content, message); updateFriendRequestStatusIfNeeded(content, message);
} }
} else if (content.getSyncMessage().isPresent()) { } else if (content.getSyncMessage().isPresent()) {
TextSecurePreferences.setMultiDevice(context, true); TextSecurePreferences.setMultiDevice(context, true);
@ -404,8 +400,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
// Loki - Handle session reset logic // Loki - Handle session reset logic
if (!envelope.isFriendRequest()) { if (!content.isFriendRequest()) {
cipher.handleSessionResetRequestIfNeeded(envelope, cipher.getSessionStatus(envelope)); cipher.handleSessionResetRequestIfNeeded(content, cipher.getSessionStatus(content));
} }
} catch (ProtocolInvalidVersionException e) { } catch (ProtocolInvalidVersionException e) {
Log.w(TAG, e); Log.w(TAG, e);
@ -1109,26 +1105,26 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(primaryDevice, url)); ApplicationContext.getInstance(context).getJobManager().add(new RetrieveProfileAvatarJob(primaryDevice, url));
} }
private void handlePairingMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) { private void handlePairingMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context); String userHexEncodedPublicKey = TextSecurePreferences.getLocalNumber(context);
if (authorisation.getType() == PairingAuthorisation.Type.REQUEST) { if (authorisation.getType() == PairingAuthorisation.Type.REQUEST) {
handlePairingRequestMessage(authorisation, envelope, content); handlePairingRequestMessage(authorisation, content);
} else if (authorisation.getSecondaryDevicePublicKey().equals(userHexEncodedPublicKey)) { } else if (authorisation.getSecondaryDevicePublicKey().equals(userHexEncodedPublicKey)) {
handlePairingAuthorisationMessage(authorisation, envelope, content); handlePairingAuthorisationMessage(authorisation, content);
} }
} }
private void handlePairingRequestMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) { private void handlePairingRequestMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
boolean isValid = isValidPairingMessage(authorisation); boolean isValid = isValidPairingMessage(authorisation);
DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared(); DeviceLinkingSession linkingSession = DeviceLinkingSession.Companion.getShared();
if (isValid && linkingSession.isListeningForLinkingRequests()) { if (isValid && linkingSession.isListeningForLinkingRequests()) {
// Loki - If we successfully received a request then we should store the PreKeyBundle // Loki - If we successfully received a request then we should store the PreKeyBundle
storePreKeyBundleIfNeeded(envelope, content); storePreKeyBundleIfNeeded(content);
linkingSession.processLinkingRequest(authorisation); linkingSession.processLinkingRequest(authorisation);
} }
} }
private void handlePairingAuthorisationMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) { private void handlePairingAuthorisationMessage(@NonNull PairingAuthorisation authorisation, @NonNull SignalServiceContent content) {
// Prepare // Prepare
boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null; boolean isSecondaryDevice = TextSecurePreferences.getMasterHexEncodedPublicKey(context) != null;
if (isSecondaryDevice) { if (isSecondaryDevice) {
@ -1147,7 +1143,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
if (authorisation.getType() != PairingAuthorisation.Type.GRANT) { return; } if (authorisation.getType() != PairingAuthorisation.Type.GRANT) { return; }
Log.d("Loki", "Received pairing authorisation message from: " + authorisation.getPrimaryDevicePublicKey() + "."); Log.d("Loki", "Received pairing authorisation message from: " + authorisation.getPrimaryDevicePublicKey() + ".");
// Save PreKeyBundle if for whatever reason we got one // Save PreKeyBundle if for whatever reason we got one
storePreKeyBundleIfNeeded(envelope, content); storePreKeyBundleIfNeeded(content);
// Process // Process
DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(authorisation); DeviceLinkingSession.Companion.getShared().processLinkingAuthorization(authorisation);
// Store the primary device's public key // Store the primary device's public key
@ -1188,7 +1184,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
} }
private void storePreKeyBundleIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) { private void storePreKeyBundleIfNeeded(@NonNull SignalServiceContent content) {
Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false); Recipient sender = Recipient.from(context, Address.fromSerialized(content.getSender()), false);
if (!sender.isGroupRecipient() && content.lokiServiceMessage.isPresent()) { if (!sender.isGroupRecipient() && content.lokiServiceMessage.isPresent()) {
LokiServiceMessage lokiMessage = content.lokiServiceMessage.get(); LokiServiceMessage lokiMessage = content.lokiServiceMessage.get();
@ -1205,7 +1201,7 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
lokiPreKeyBundleDatabase.setPreKeyBundle(content.getSender(), preKeyBundle); lokiPreKeyBundleDatabase.setPreKeyBundle(content.getSender(), preKeyBundle);
// Loki - If we received a friend request, but we were already friends with this user, then reset the session // Loki - If we received a friend request, but we were already friends with this user, then reset the session
if (envelope.isFriendRequest()) { if (content.isFriendRequest()) {
long threadID = threadDatabase.getThreadIdIfExistsFor(sender); long threadID = threadDatabase.getThreadIdIfExistsFor(sender);
if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) { if (lokiThreadDatabase.getFriendRequestStatus(threadID) == LokiThreadFriendRequestStatus.FRIENDS) {
resetSession(content.getSender(), threadID); resetSession(content.getSender(), threadID);
@ -1218,9 +1214,9 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
} }
} }
private void acceptFriendRequestIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content) { private void acceptFriendRequestIfNeeded(@NonNull SignalServiceContent content) {
// If we get anything other than a friend request, we can assume that we have a session with the other user // If we get anything other than a friend request, we can assume that we have a session with the other user
if (envelope.isFriendRequest() || isGroupChatMessage(content)) { return; } if (content.isFriendRequest() || isGroupChatMessage(content)) { return; }
becomeFriendsWithContact(content.getSender(), true); becomeFriendsWithContact(content.getSender(), true);
} }
@ -1251,8 +1247,8 @@ public class PushDecryptJob extends BaseJob implements InjectableType {
}); });
} }
private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceEnvelope envelope, @NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) { private void updateFriendRequestStatusIfNeeded(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) {
if (!envelope.isFriendRequest() || message.isGroupUpdate()) { return; } if (!content.isFriendRequest() || message.isGroupUpdate()) { return; }
// This handles the case where another user sends us a regular message without authorisation // This handles the case where another user sends us a regular message without authorisation
Promise<Boolean, Exception> promise = PromiseUtil.timeout(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), 8000); Promise<Boolean, Exception> promise = PromiseUtil.timeout(MultiDeviceUtilities.shouldAutomaticallyBecomeFriendsWithDevice(content.getSender(), context), 8000);
boolean shouldBecomeFriends = PromiseUtil.get(promise, false); boolean shouldBecomeFriends = PromiseUtil.get(promise, false);

View File

@ -164,7 +164,7 @@ class LokiPublicChatPoller(private val context: Context, private val group: Loki
val senderPublicKey = primaryDevice ?: message.hexEncodedPublicKey val senderPublicKey = primaryDevice ?: message.hexEncodedPublicKey
val serviceDataMessage = getDataMessage(message) val serviceDataMessage = getDataMessage(message)
val serviceContent = SignalServiceContent(serviceDataMessage, senderPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false) val serviceContent = SignalServiceContent(serviceDataMessage, senderPublicKey, SignalServiceAddress.DEFAULT_DEVICE_ID, message.timestamp, false, false)
if (serviceDataMessage.quote.isPresent || (serviceDataMessage.attachments.isPresent && serviceDataMessage.attachments.get().size > 0) || serviceDataMessage.previews.isPresent) { 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)) PushDecryptJob(context).handleMediaMessage(serviceContent, serviceDataMessage, Optional.absent(), Optional.of(message.serverID))
} else { } else {

View File

@ -66,7 +66,7 @@ class LokiRSSFeedPoller(private val context: Context, private val feed: LokiRSSF
val id = feed.id.toByteArray() val id = feed.id.toByteArray()
val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null) val x1 = SignalServiceGroup(SignalServiceGroup.Type.UPDATE, id, null, null, null)
val x2 = SignalServiceDataMessage(timestamp, x1, null, body) val x2 = SignalServiceDataMessage(timestamp, x1, null, body)
val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false) val x3 = SignalServiceContent(x2, "Loki", SignalServiceAddress.DEFAULT_DEVICE_ID, timestamp, false, false)
PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent()) PushDecryptJob(context).handleTextMessage(x3, x2, Optional.absent(), Optional.absent())
} }
} catch (exception: Exception) { } catch (exception: Exception) {

View File

@ -13,6 +13,7 @@ import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.IdentityKeyUtil import org.thoughtcrime.securesms.crypto.IdentityKeyUtil
import org.thoughtcrime.securesms.crypto.PreKeyUtil import org.thoughtcrime.securesms.crypto.PreKeyUtil
import org.thoughtcrime.securesms.crypto.ProfileKeyUtil import org.thoughtcrime.securesms.crypto.ProfileKeyUtil
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
import org.thoughtcrime.securesms.database.Address import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.logging.Log import org.thoughtcrime.securesms.logging.Log
@ -128,7 +129,8 @@ fun sendPairingAuthorisationMessage(context: Context, contactHexEncodedPublicKey
return try { return try {
Log.d("Loki", "Sending authorisation message to: $contactHexEncodedPublicKey.") Log.d("Loki", "Sending authorisation message to: $contactHexEncodedPublicKey.")
val result = messageSender.sendMessage(0, address, Optional.absent<UnidentifiedAccessPair>(), message.build()) val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(contactHexEncodedPublicKey), false))
val result = messageSender.sendMessage(0, address, udAccess, message.build())
if (result.success == null) { if (result.success == null) {
val exception = when { val exception = when {
result.isNetworkFailure -> "Failed to send authorisation message due to a network error." result.isNetworkFailure -> "Failed to send authorisation message due to a network error."

View File

@ -1,12 +1,15 @@
package org.thoughtcrime.securesms.loki package org.thoughtcrime.securesms.loki
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
import org.thoughtcrime.securesms.crypto.UnidentifiedAccessUtil
import org.thoughtcrime.securesms.database.Address
import org.thoughtcrime.securesms.database.DatabaseFactory import org.thoughtcrime.securesms.database.DatabaseFactory
import org.thoughtcrime.securesms.jobmanager.Data import org.thoughtcrime.securesms.jobmanager.Data
import org.thoughtcrime.securesms.jobmanager.Job import org.thoughtcrime.securesms.jobmanager.Job
import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint import org.thoughtcrime.securesms.jobmanager.impl.NetworkConstraint
import org.thoughtcrime.securesms.jobs.BaseJob import org.thoughtcrime.securesms.jobs.BaseJob
import org.thoughtcrime.securesms.logging.Log import org.thoughtcrime.securesms.logging.Log
import org.thoughtcrime.securesms.recipients.Recipient
import org.whispersystems.libsignal.util.guava.Optional import org.whispersystems.libsignal.util.guava.Optional
import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage
@ -99,7 +102,8 @@ class PushBackgroundMessageSendJob private constructor(
val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender() val messageSender = ApplicationContext.getInstance(context).communicationModule.provideSignalMessageSender()
val address = SignalServiceAddress(recipient) val address = SignalServiceAddress(recipient)
try { try {
messageSender.sendMessage(-1, address, Optional.absent<UnidentifiedAccessPair>(), dataMessage.build()) // The message ID doesn't matter val udAccess = UnidentifiedAccessUtil.getAccessFor(context, Recipient.from(context, Address.fromSerialized(recipient), false))
messageSender.sendMessage(-1, address, udAccess, dataMessage.build()) // The message ID doesn't matter
} catch (e: Exception) { } catch (e: Exception) {
Log.d("Loki", "Failed to send background message to: ${recipient}.") Log.d("Loki", "Failed to send background message to: ${recipient}.")
throw e throw e