diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java index 5fb05e84e9..b43ed43d69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/SignalProtocolStoreImpl.java @@ -19,6 +19,7 @@ import org.whispersystems.libsignal.state.SignedPreKeyRecord; import org.whispersystems.libsignal.state.SignedPreKeyStore; import org.whispersystems.signalservice.api.SignalServiceDataStore; import org.whispersystems.signalservice.api.SignalServiceSessionStore; +import org.whispersystems.signalservice.api.messages.InvalidRegistrationIdException; import org.whispersystems.signalservice.api.push.DistributionId; import java.util.Collection; @@ -105,7 +106,7 @@ public class SignalProtocolStoreImpl implements SignalServiceDataStore { } @Override - public Set getAllAddressesWithActiveSessions(List addressNames) { + public Set getAllAddressesWithActiveSessions(List addressNames) throws InvalidRegistrationIdException { return sessionStore.getAllAddressesWithActiveSessions(addressNames); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java index 71f357ad5e..9be4da0f3b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java +++ b/app/src/main/java/org/thoughtcrime/securesms/crypto/storage/TextSecureSessionStore.java @@ -15,6 +15,7 @@ import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.protocol.CiphertextMessage; import org.whispersystems.libsignal.state.SessionRecord; import org.whispersystems.signalservice.api.SignalServiceSessionStore; +import org.whispersystems.signalservice.api.messages.InvalidRegistrationIdException; import java.util.List; import java.util.Set; @@ -26,7 +27,7 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { private static final Object LOCK = new Object(); - @NonNull private final Context context; + @NonNull private final Context context; public TextSecureSessionStore(@NonNull Context context) { this.context = context; @@ -101,9 +102,17 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { } @Override - public Set getAllAddressesWithActiveSessions(List addressNames) { + public Set getAllAddressesWithActiveSessions(List addressNames) throws InvalidRegistrationIdException { synchronized (LOCK) { List rows = DatabaseFactory.getSessionDatabase(context).getAllFor(addressNames); + + boolean hasInvalidRegistrationId = rows.stream() + .map(SessionDatabase.SessionRow::getRecord) + .anyMatch(record -> !isValidRegistrationId(record.getRemoteRegistrationId())); + if (hasInvalidRegistrationId) { + throw new InvalidRegistrationIdException(); + } + return rows.stream() .filter(row -> isActive(row.getRecord())) .map(row -> new SignalProtocolAddress(row.getAddress(), row.getDeviceId())) @@ -165,4 +174,8 @@ public class TextSecureSessionStore implements SignalServiceSessionStore { record.hasSenderChain() && record.getSessionVersion() == CiphertextMessage.CURRENT_VERSION; } + + private static boolean isValidRegistrationId(int registrationId) { + return (registrationId & 0x3fff) == registrationId; + } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java b/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java index 6aa1bd0a90..b6c862f570 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/GroupSendUtil.java @@ -31,6 +31,7 @@ import org.whispersystems.signalservice.api.crypto.ContentHint; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; +import org.whispersystems.signalservice.api.messages.InvalidRegistrationIdException; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage; import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage; @@ -237,7 +238,10 @@ public final class GroupSendUtil { Log.w(TAG, "No session. Falling back to legacy sends.", e); legacyTargets.addAll(senderKeyTargets); } catch (InvalidKeyException e) { - Log.w(TAG, "Invalid Key. Falling back to legacy sends.", e); + Log.w(TAG, "Invalid key. Falling back to legacy sends.", e); + legacyTargets.addAll(senderKeyTargets); + } catch (InvalidRegistrationIdException e) { + Log.w(TAG, "Invalid registrationId. Falling back to legacy sends.", e); legacyTargets.addAll(senderKeyTargets); } } @@ -308,7 +312,7 @@ public final class GroupSendUtil { @NonNull List targets, @NonNull List access, boolean isRecipientUpdate) - throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException; + throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException, InvalidRegistrationIdException; @NonNull List sendLegacy(@NonNull SignalServiceMessageSender messageSender, @NonNull List targets, @@ -355,7 +359,7 @@ public final class GroupSendUtil { @NonNull List targets, @NonNull List access, boolean isRecipientUpdate) - throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException + throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException, InvalidRegistrationIdException { return messageSender.sendGroupDataMessage(distributionId, targets, access, isRecipientUpdate, contentHint, message); } @@ -411,7 +415,7 @@ public final class GroupSendUtil { @NonNull List targets, @NonNull List access, boolean isRecipientUpdate) - throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException + throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException, InvalidRegistrationIdException { messageSender.sendGroupTyping(distributionId, targets, access, message); return targets.stream().map(a -> SendMessageResult.success(a, Collections.emptyList(), true, false, -1, Optional.absent())).collect(Collectors.toList()); @@ -465,7 +469,7 @@ public final class GroupSendUtil { @NonNull List targets, @NonNull List access, boolean isRecipientUpdate) - throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException + throws NoSessionException, UntrustedIdentityException, InvalidKeyException, IOException, InvalidRegistrationIdException { return messageSender.sendCallMessage(distributionId, targets, access, message); } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index d7dfcc5f6d..8728c48648 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -31,6 +31,7 @@ import org.whispersystems.signalservice.api.crypto.SignalSessionBuilder; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess; import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair; import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException; +import org.whispersystems.signalservice.api.messages.InvalidRegistrationIdException; import org.whispersystems.signalservice.api.messages.SendMessageResult; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; @@ -249,7 +250,7 @@ public class SignalServiceMessageSender { List recipients, List unidentifiedAccess, SignalServiceTypingMessage message) - throws IOException, UntrustedIdentityException, InvalidKeyException, NoSessionException + throws IOException, UntrustedIdentityException, InvalidKeyException, NoSessionException, InvalidRegistrationIdException { Content content = createTypingContent(message); sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp(), content, ContentHint.IMPLICIT, message.getGroupId().orNull(), true); @@ -289,7 +290,7 @@ public class SignalServiceMessageSender { List recipients, List unidentifiedAccess, SignalServiceCallMessage message) - throws IOException, UntrustedIdentityException, InvalidKeyException, NoSessionException + throws IOException, UntrustedIdentityException, InvalidKeyException, NoSessionException, InvalidRegistrationIdException { Content content = createCallContent(message); return sendGroupMessage(distributionId, recipients, unidentifiedAccess, message.getTimestamp().get(), content, ContentHint.IMPLICIT, message.getGroupId().get(), false); @@ -420,7 +421,7 @@ public class SignalServiceMessageSender { boolean isRecipientUpdate, ContentHint contentHint, SignalServiceDataMessage message) - throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException + throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException { Log.d(TAG, "[" + message.getTimestamp() + "] Sending a group data message to " + recipients.size() + " recipients."); @@ -1673,7 +1674,7 @@ public class SignalServiceMessageSender { ContentHint contentHint, byte[] groupId, boolean online) - throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException + throws IOException, UntrustedIdentityException, NoSessionException, InvalidKeyException, InvalidRegistrationIdException { if (recipients.isEmpty()) { Log.w(TAG, "[sendGroupMessage] Empty recipient list!"); @@ -1797,7 +1798,7 @@ public class SignalServiceMessageSender { throw new IOException("Failed to resolve conflicts after " + RETRY_COUNT + " attempts!"); } - private GroupTargetInfo buildGroupTargetInfo(List recipients) { + private GroupTargetInfo buildGroupTargetInfo(List recipients) throws InvalidRegistrationIdException { List addressNames = recipients.stream().map(SignalServiceAddress::getIdentifier).collect(Collectors.toList()); Set destinations = store.getAllAddressesWithActiveSessions(addressNames); Map> devicesByAddressName = new HashMap<>(); diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceSessionStore.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceSessionStore.java index 46f570f54b..4ecd6e81b3 100644 --- a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceSessionStore.java +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/SignalServiceSessionStore.java @@ -2,6 +2,7 @@ package org.whispersystems.signalservice.api; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.state.SessionStore; +import org.whispersystems.signalservice.api.messages.InvalidRegistrationIdException; import java.util.List; import java.util.Set; @@ -12,5 +13,5 @@ import java.util.Set; */ public interface SignalServiceSessionStore extends SessionStore { void archiveSession(SignalProtocolAddress address); - Set getAllAddressesWithActiveSessions(List addressNames); + Set getAllAddressesWithActiveSessions(List addressNames) throws InvalidRegistrationIdException; } diff --git a/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/InvalidRegistrationIdException.java b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/InvalidRegistrationIdException.java new file mode 100644 index 0000000000..de340c595e --- /dev/null +++ b/libsignal/service/src/main/java/org/whispersystems/signalservice/api/messages/InvalidRegistrationIdException.java @@ -0,0 +1,7 @@ +package org.whispersystems.signalservice.api.messages; + +/** + * Indicates that a session has a bad registration ID. + */ +public class InvalidRegistrationIdException extends Exception { +}