Add kotlin/proto level message processing.

This commit is contained in:
Cody Henthorne
2023-03-30 11:45:13 -04:00
committed by Alex Hart
parent 28f27915c5
commit 2e45bd719a
43 changed files with 4505 additions and 84 deletions

View File

@@ -11,6 +11,7 @@ import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Optional;
import java.util.Set;
/**
* Sometimes a message that is referencing another message can arrive out of order. In these cases,
@@ -19,7 +20,8 @@ import java.util.Optional;
*/
public final class EarlyMessageCache {
private final LRUCache<ServiceMessageId, List<SignalServiceContent>> cache = new LRUCache<>(100);
private final LRUCache<ServiceMessageId, List<SignalServiceContent>> cache = new LRUCache<>(100);
private final LRUCache<ServiceMessageId, List<EarlyMessageCacheEntry>> cacheV2 = new LRUCache<>(100);
/**
* @param targetSender The sender of the message this message depends on.
@@ -38,8 +40,25 @@ public final class EarlyMessageCache {
cache.put(messageId, contentList);
}
public synchronized void store(@NonNull RecipientId targetSender,
long targetSentTimestamp,
@NonNull EarlyMessageCacheEntry cacheEntry)
{
ServiceMessageId messageId = new ServiceMessageId(targetSender, targetSentTimestamp);
List<EarlyMessageCacheEntry> envelopeList = cacheV2.get(messageId);
if (envelopeList == null) {
envelopeList = new LinkedList<>();
}
envelopeList.add(cacheEntry);
cacheV2.put(messageId, envelopeList);
}
/**
* Returns and removes any content that is dependent on the provided message id.
*
* @param sender The sender of the message in question.
* @param sentTimestamp The sent timestamp of the message in question.
*/
@@ -47,11 +66,17 @@ public final class EarlyMessageCache {
return Optional.ofNullable(cache.remove(new ServiceMessageId(sender, sentTimestamp)));
}
public synchronized Optional<List<EarlyMessageCacheEntry>> retrieveV2(@NonNull RecipientId sender, long sentTimestamp) {
return Optional.ofNullable(cacheV2.remove(new ServiceMessageId(sender, sentTimestamp)));
}
/**
* Returns a collection of all of the {@link ServiceMessageId}s referenced in the cache at the moment of inquiry.
* Caution: There is no guarantee that this list will be relevant for any amount of time afterwards.
*/
public synchronized @NonNull Collection<ServiceMessageId> getAllReferencedIds() {
return new HashSet<>(cache.keySet());
Set<ServiceMessageId> allIds = new HashSet<>(cache.keySet());
allIds.addAll(cacheV2.keySet());
return allIds;
}
}

View File

@@ -0,0 +1,16 @@
package org.thoughtcrime.securesms.util
import org.whispersystems.signalservice.api.crypto.EnvelopeMetadata
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Content
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.Envelope
/**
* The tuple of information needed to process a message. Used to in [EarlyMessageCache]
* to store potentially out-of-order messages.
*/
data class EarlyMessageCacheEntry(
val envelope: Envelope,
val content: Content,
val metadata: EnvelopeMetadata,
val serverDeliveredTimestamp: Long
)

View File

@@ -6,7 +6,6 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
import org.signal.core.util.StringUtil;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.zkgroup.InvalidInputException;
@@ -16,12 +15,14 @@ import org.thoughtcrime.securesms.database.GroupTable;
import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.groups.GroupId;
import org.thoughtcrime.securesms.messages.SignalServiceProtoUtil;
import org.thoughtcrime.securesms.mms.MessageGroupContext;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceGroupV2;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import java.io.IOException;
import java.util.List;
@@ -55,6 +56,23 @@ public final class GroupUtil {
}
}
public static @Nullable SignalServiceProtos.GroupContextV2 getGroupContextIfPresent(@NonNull SignalServiceProtos.Content content) {
if (content.hasDataMessage() && SignalServiceProtoUtil.INSTANCE.getHasGroupContext(content.getDataMessage())) {
return content.getDataMessage().getGroupV2();
} else if (content.hasSyncMessage() &&
content.getSyncMessage().hasSent() &&
content.getSyncMessage().getSent().hasMessage() &&
SignalServiceProtoUtil.INSTANCE.getHasGroupContext(content.getSyncMessage().getSent().getMessage()))
{
return content.getSyncMessage().getSent().getMessage().getGroupV2();
} else if (content.hasStoryMessage() && SignalServiceProtoUtil.INSTANCE.isValid(content.getStoryMessage().getGroup())) {
return content.getStoryMessage().getGroup();
} else {
return null;
}
}
/**
* Result may be a v1 or v2 GroupId.
*/

View File

@@ -7,8 +7,10 @@ import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import org.signal.core.util.concurrent.SignalExecutors;
import org.signal.core.util.concurrent.SimpleTask;
import org.signal.core.util.logging.Log;
import org.signal.libsignal.protocol.IdentityKey;
import org.signal.libsignal.protocol.InvalidKeyException;
import org.signal.libsignal.protocol.SignalProtocolAddress;
import org.signal.libsignal.protocol.state.SessionRecord;
import org.signal.libsignal.protocol.state.SessionStore;
@@ -23,7 +25,6 @@ import org.thoughtcrime.securesms.database.SignalDatabase;
import org.thoughtcrime.securesms.database.model.GroupRecord;
import org.thoughtcrime.securesms.database.model.IdentityRecord;
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
import org.thoughtcrime.securesms.keyvalue.SignalStore;
import org.thoughtcrime.securesms.mms.MmsException;
import org.thoughtcrime.securesms.mms.OutgoingMessage;
import org.thoughtcrime.securesms.notifications.v2.ConversationId;
@@ -35,10 +36,11 @@ import org.thoughtcrime.securesms.sms.IncomingIdentityVerifiedMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.util.concurrent.ListenableFuture;
import org.thoughtcrime.securesms.util.concurrent.SettableFuture;
import org.signal.core.util.concurrent.SimpleTask;
import org.whispersystems.signalservice.api.SignalSessionLock;
import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
import java.util.List;
import java.util.Optional;
@@ -175,6 +177,28 @@ public final class IdentityUtil {
}
}
public static void processVerifiedMessage(Context context, SignalServiceProtos.Verified verified) throws InvalidKeyException {
SignalServiceAddress destination = new SignalServiceAddress(ServiceId.parseOrThrow(verified.getDestinationUuid()));
IdentityKey identityKey = new IdentityKey(verified.getIdentityKey().toByteArray(), 0);
VerifiedMessage.VerifiedState state;
switch (verified.getState()) {
case DEFAULT:
state = VerifiedMessage.VerifiedState.DEFAULT;
break;
case VERIFIED:
state = VerifiedMessage.VerifiedState.VERIFIED;
break;
case UNVERIFIED:
state = VerifiedMessage.VerifiedState.UNVERIFIED;
break;
default:
throw new IllegalArgumentException();
}
processVerifiedMessage(context, new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis()));
}
public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) {
try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) {
SignalIdentityKeyStore identityStore = ApplicationDependencies.getProtocolStore().aci().identities();

View File

@@ -6,6 +6,7 @@ import com.annimon.stream.Stream;
import org.thoughtcrime.securesms.database.model.MessageRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
@@ -18,18 +19,23 @@ public final class RemoteDeleteUtil {
private RemoteDeleteUtil() {}
public static boolean isValidReceive(@NonNull MessageRecord targetMessage, @NonNull Recipient deleteSender, long deleteServerTimestamp) {
boolean isValidIncomingOutgoing = (deleteSender.isSelf() && targetMessage.isOutgoing()) ||
(!deleteSender.isSelf() && !targetMessage.isOutgoing());
return isValidReceive(targetMessage, deleteSender.getId(), deleteServerTimestamp);
}
boolean isValidSender = targetMessage.getIndividualRecipient().equals(deleteSender) ||
deleteSender.isSelf() && targetMessage.isOutgoing();
public static boolean isValidReceive(@NonNull MessageRecord targetMessage, @NonNull RecipientId deleteSenderId, long deleteServerTimestamp) {
boolean selfIsDeleteSender = isSelf(deleteSenderId);
long messageTimestamp = deleteSender.isSelf() && targetMessage.isOutgoing() ? targetMessage.getDateSent()
: targetMessage.getServerTimestamp();
boolean isValidIncomingOutgoing = (selfIsDeleteSender && targetMessage.isOutgoing()) ||
(!selfIsDeleteSender && !targetMessage.isOutgoing());
boolean isValidSender = targetMessage.getIndividualRecipient().getId().equals(deleteSenderId) || selfIsDeleteSender && targetMessage.isOutgoing();
long messageTimestamp = selfIsDeleteSender && targetMessage.isOutgoing() ? targetMessage.getDateSent()
: targetMessage.getServerTimestamp();
return isValidIncomingOutgoing &&
isValidSender &&
(((deleteServerTimestamp - messageTimestamp) < RECEIVE_THRESHOLD) || (deleteSender.isSelf() && targetMessage.isOutgoing()));
isValidSender &&
(((deleteServerTimestamp - messageTimestamp) < RECEIVE_THRESHOLD) || (selfIsDeleteSender && targetMessage.isOutgoing()));
}
public static boolean isValidSend(@NonNull Collection<MessageRecord> targetMessages, long currentTime) {
@@ -38,13 +44,17 @@ public final class RemoteDeleteUtil {
}
private static boolean isValidSend(MessageRecord message, long currentTime) {
return !message.isUpdate() &&
message.isOutgoing() &&
message.isPush() &&
return !message.isUpdate() &&
message.isOutgoing() &&
message.isPush() &&
(!message.getRecipient().isGroup() || message.getRecipient().isActiveGroup()) &&
!message.isRemoteDelete() &&
!MessageRecordUtil.hasGiftBadge(message) &&
!message.isPaymentNotification() &&
!message.isRemoteDelete() &&
!MessageRecordUtil.hasGiftBadge(message) &&
!message.isPaymentNotification() &&
(((currentTime - message.getDateSent()) < SEND_THRESHOLD) || message.getRecipient().isSelf());
}
private static boolean isSelf(@NonNull RecipientId recipientId) {
return Recipient.isSelfSet() && Recipient.self().getId().equals(recipientId);
}
}