From 9a8094cb8a3a4526e4dbda0064800786756144a0 Mon Sep 17 00:00:00 2001 From: Alan Evans Date: Mon, 20 Apr 2020 12:01:31 -0300 Subject: [PATCH] Guard against malformed group ids. --- .../securesms/GroupCreateActivity.java | 2 +- .../securesms/database/GroupDatabase.java | 5 +- .../securesms/database/RecipientDatabase.java | 10 +-- .../securesms/groups/BadGroupIdException.java | 15 ++++ .../securesms/groups/GroupId.java | 62 ++++++++++++++--- .../groups/GroupV1MessageProcessor.java | 14 ++-- .../PendingMemberInvitesActivity.java | 2 +- .../PendingMemberInvitesFragment.java | 2 +- .../jobs/AvatarGroupsV1DownloadJob.java | 2 +- .../jobs/AvatarGroupsV2DownloadJob.java | 2 +- .../securesms/jobs/LeaveGroupJob.java | 2 +- .../securesms/jobs/PushDecryptMessageJob.java | 15 +++- .../securesms/jobs/PushGroupUpdateJob.java | 3 +- .../securesms/jobs/PushProcessMessageJob.java | 55 ++++++++++----- .../securesms/jobs/RequestGroupInfoJob.java | 2 +- .../securesms/mms/IncomingMediaMessage.java | 2 +- .../securesms/recipients/Recipient.java | 2 +- .../securesms/sms/IncomingTextMessage.java | 2 +- .../storage/GroupV1ConflictMerger.java | 4 +- .../securesms/util/GroupUtil.java | 22 +++++- .../securesms/groups/GroupIdTest.java | 69 ++++++++++--------- 21 files changed, 200 insertions(+), 94 deletions(-) create mode 100644 app/src/main/java/org/thoughtcrime/securesms/groups/BadGroupIdException.java diff --git a/app/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java b/app/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java index 796f4ab33e..a0f9ee8664 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/GroupCreateActivity.java @@ -211,7 +211,7 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity } private void initializeExistingGroup() { - final GroupId groupId = GroupId.parseNullable(getIntent().getStringExtra(GROUP_ID_EXTRA)); + final GroupId groupId = GroupId.parseNullableOrThrow(getIntent().getStringExtra(GROUP_ID_EXTRA)); if (groupId != null) { new FillExistingGroupInfoAsyncTask(this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, groupId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java index 91715cc92b..010461399b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/GroupDatabase.java @@ -25,7 +25,6 @@ import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; -import org.thoughtcrime.securesms.util.Util; import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; @@ -186,7 +185,7 @@ public final class GroupDatabase extends Database { null, null, null); try { if (cursor != null && cursor.moveToNext()) { - return GroupId.parse(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))) + return GroupId.parseOrThrow(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))) .requireMms(); } else { GroupId.Mms groupId = GroupId.createMms(new SecureRandom()); @@ -519,7 +518,7 @@ public final class GroupDatabase extends Database { return null; } - return new GroupRecord(GroupId.parse(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))), + return new GroupRecord(GroupId.parseOrThrow(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))), RecipientId.from(cursor.getLong(cursor.getColumnIndexOrThrow(RECIPIENT_ID))), cursor.getString(cursor.getColumnIndexOrThrow(TITLE)), cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java index 2848c72e69..6e9c5a42d6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/RecipientDatabase.java @@ -561,7 +561,7 @@ public class RecipientDatabase extends Database { for (SignalGroupV1Record insert : groupV1Inserts) { db.insertOrThrow(TABLE_NAME, null, getValuesForStorageGroupV1(insert)); - Recipient recipient = Recipient.externalGroup(context, GroupId.v1(insert.getGroupId())); + Recipient recipient = Recipient.externalGroup(context, GroupId.v1orThrow(insert.getGroupId())); threadDatabase.setArchived(recipient.getId(), insert.isArchived()); recipient.live().refresh(); @@ -575,7 +575,7 @@ public class RecipientDatabase extends Database { throw new AssertionError("Had an update, but it didn't match any rows!"); } - Recipient recipient = Recipient.externalGroup(context, GroupId.v1(update.getOld().getGroupId())); + Recipient recipient = Recipient.externalGroup(context, GroupId.v1orThrow(update.getOld().getGroupId())); threadDatabase.setArchived(recipient.getId(), update.getNew().isArchived()); recipient.live().refresh(); @@ -670,7 +670,7 @@ public class RecipientDatabase extends Database { private static @NonNull ContentValues getValuesForStorageGroupV1(@NonNull SignalGroupV1Record groupV1) { ContentValues values = new ContentValues(); - values.put(GROUP_ID, GroupId.v1(groupV1.getGroupId()).toString()); + values.put(GROUP_ID, GroupId.v1orThrow(groupV1.getGroupId()).toString()); values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId()); values.put(PROFILE_SHARING, groupV1.isProfileSharingEnabled() ? "1" : "0"); values.put(BLOCKED, groupV1.isBlocked() ? "1" : "0"); @@ -733,7 +733,7 @@ public class RecipientDatabase extends Database { String username = cursor.getString(cursor.getColumnIndexOrThrow(USERNAME)); String e164 = cursor.getString(cursor.getColumnIndexOrThrow(PHONE)); String email = cursor.getString(cursor.getColumnIndexOrThrow(EMAIL)); - GroupId groupId = GroupId.parseNullable(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))); + GroupId groupId = GroupId.parseNullableOrThrow(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID))); int groupType = cursor.getInt(cursor.getColumnIndexOrThrow(GROUP_TYPE)); boolean blocked = cursor.getInt(cursor.getColumnIndexOrThrow(BLOCKED)) == 1; String messageRingtone = cursor.getString(cursor.getColumnIndexOrThrow(MESSAGE_RINGTONE)); @@ -1406,7 +1406,7 @@ public class RecipientDatabase extends Database { db.update(TABLE_NAME, setBlocked, UUID + " = ?", new String[] { uuid }); } - List groupIdStrings = Stream.of(groupIds).map(GroupId::v1).toList(); + List groupIdStrings = Stream.of(groupIds).map(GroupId::v1orThrow).toList(); for (GroupId.V1 groupId : groupIdStrings) { db.update(TABLE_NAME, setBlocked, GROUP_ID + " = ?", new String[] { groupId.toString() }); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/BadGroupIdException.java b/app/src/main/java/org/thoughtcrime/securesms/groups/BadGroupIdException.java new file mode 100644 index 0000000000..163a46999e --- /dev/null +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/BadGroupIdException.java @@ -0,0 +1,15 @@ +package org.thoughtcrime.securesms.groups; + +public final class BadGroupIdException extends Exception { + BadGroupIdException(String message) { + super(message); + } + + BadGroupIdException() { + super(); + } + + BadGroupIdException(Exception e) { + super(e); + } +} diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupId.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupId.java index 42cea40ea3..b3ce7cb3d8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupId.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupId.java @@ -30,30 +30,46 @@ public abstract class GroupId { return new GroupId.Mms(mmsGroupIdBytes); } - public static @NonNull GroupId.V1 v1(byte[] gv1GroupIdBytes) { + public static @NonNull GroupId.V1 v1orThrow(byte[] gv1GroupIdBytes) { + try { + return v1(gv1GroupIdBytes); + } catch (BadGroupIdException e) { + throw new AssertionError(e); + } + } + + public static @NonNull GroupId.V1 v1(byte[] gv1GroupIdBytes) throws BadGroupIdException { if (gv1GroupIdBytes.length == V2_BYTE_LENGTH) { - throw new AssertionError(); + throw new BadGroupIdException(); } return new GroupId.V1(gv1GroupIdBytes); } public static GroupId.V1 createV1(@NonNull SecureRandom secureRandom) { - return v1(Util.getSecretBytes(secureRandom, V1_MMS_BYTE_LENGTH)); + return v1orThrow(Util.getSecretBytes(secureRandom, V1_MMS_BYTE_LENGTH)); } public static GroupId.Mms createMms(@NonNull SecureRandom secureRandom) { return mms(Util.getSecretBytes(secureRandom, MMS_BYTE_LENGTH)); } - public static GroupId.V2 v2(@NonNull byte[] bytes) { + public static GroupId.V2 v2orThrow(@NonNull byte[] bytes) { + try { + return v2(bytes); + } catch (BadGroupIdException e) { + throw new AssertionError(e); + } + } + + public static GroupId.V2 v2(@NonNull byte[] bytes) throws BadGroupIdException { if (bytes.length != V2_BYTE_LENGTH) { - throw new AssertionError(); + throw new BadGroupIdException(); } return new GroupId.V2(bytes); } public static GroupId.V2 v2(@NonNull GroupIdentifier groupIdentifier) { - return v2(groupIdentifier.serialize()); + return v2orThrow(groupIdentifier.serialize()); } public static GroupId.V2 v2(@NonNull GroupMasterKey masterKey) { @@ -62,25 +78,41 @@ public abstract class GroupId { .getGroupIdentifier()); } - public static GroupId.Push push(byte[] bytes) { + public static GroupId.Push push(byte[] bytes) throws BadGroupIdException { return bytes.length == V2_BYTE_LENGTH ? v2(bytes) : v1(bytes); } - public static @NonNull GroupId parse(@NonNull String encodedGroupId) { + public static GroupId.Push pushOrThrow(byte[] bytes) { + try { + return push(bytes); + } catch (BadGroupIdException e) { + throw new AssertionError(e); + } + } + + public static @NonNull GroupId parseOrThrow(@NonNull String encodedGroupId) { + try { + return parse(encodedGroupId); + } catch (BadGroupIdException e) { + throw new AssertionError(e); + } + } + + public static @NonNull GroupId parse(@NonNull String encodedGroupId) throws BadGroupIdException { try { if (!isEncodedGroup(encodedGroupId)) { - throw new IOException("Invalid encoding"); + throw new BadGroupIdException("Invalid encoding"); } byte[] bytes = extractDecodedId(encodedGroupId); return encodedGroupId.startsWith(ENCODED_MMS_GROUP_PREFIX) ? mms(bytes) : push(bytes); } catch (IOException e) { - throw new AssertionError(e); + throw new BadGroupIdException(e); } } - public static @Nullable GroupId parseNullable(@Nullable String encodedGroupId) { + public static @Nullable GroupId parseNullable(@Nullable String encodedGroupId) throws BadGroupIdException { if (encodedGroupId == null) { return null; } @@ -88,6 +120,14 @@ public abstract class GroupId { return parse(encodedGroupId); } + public static @Nullable GroupId parseNullableOrThrow(@Nullable String encodedGroupId) { + if (encodedGroupId == null) { + return null; + } + + return parseOrThrow(encodedGroupId); + } + public static boolean isEncodedGroup(@NonNull String groupId) { return groupId.startsWith(ENCODED_SIGNAL_GROUP_PREFIX) || groupId.startsWith(ENCODED_MMS_GROUP_PREFIX); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java index 004172fbf9..c26dfffc30 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/GroupV1MessageProcessor.java @@ -70,7 +70,7 @@ public final class GroupV1MessageProcessor { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); SignalServiceGroup group = groupV1.get(); - GroupId id = GroupId.v1(group.getGroupId()); + GroupId id = GroupId.v1orThrow(group.getGroupId()); Optional record = database.getGroup(id); if (record.isPresent() && group.getType() == Type.UPDATE) { @@ -93,7 +93,7 @@ public final class GroupV1MessageProcessor { boolean outgoing) { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - GroupId.V1 id = GroupId.v1(group.getGroupId()); + GroupId.V1 id = GroupId.v1orThrow(group.getGroupId()); GroupContext.Builder builder = createGroupContext(group); builder.setType(GroupContext.Type.UPDATE); @@ -127,7 +127,7 @@ public final class GroupV1MessageProcessor { { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - GroupId.V1 id = GroupId.v1(group.getGroupId()); + GroupId.V1 id = GroupId.v1orThrow(group.getGroupId()); Set recordMembers = new HashSet<>(groupRecord.getMembers()); Set messageMembers = new HashSet<>(); @@ -203,7 +203,7 @@ public final class GroupV1MessageProcessor { boolean outgoing) { GroupDatabase database = DatabaseFactory.getGroupDatabase(context); - GroupId id = GroupId.v1(group.getGroupId()); + GroupId id = GroupId.v1orThrow(group.getGroupId()); List members = record.getMembers(); GroupContext.Builder builder = createGroupContext(group); @@ -228,13 +228,13 @@ public final class GroupV1MessageProcessor { { if (group.getAvatar().isPresent()) { ApplicationDependencies.getJobManager() - .add(new AvatarGroupsV1DownloadJob(GroupId.v1(group.getGroupId()))); + .add(new AvatarGroupsV1DownloadJob(GroupId.v1orThrow(group.getGroupId()))); } try { if (outgoing) { MmsDatabase mmsDatabase = DatabaseFactory.getMmsDatabase(context); - RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1(group.getGroupId())); + RecipientId recipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(GroupId.v1orThrow(group.getGroupId())); Recipient recipient = Recipient.resolved(recipientId); OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(recipient, storage, null, content.getTimestamp(), 0, false, null, Collections.emptyList(), Collections.emptyList()); long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipient); @@ -246,7 +246,7 @@ public final class GroupV1MessageProcessor { } else { SmsDatabase smsDatabase = DatabaseFactory.getSmsDatabase(context); String body = Base64.encodeBytes(storage.toByteArray()); - IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerTimestamp(), body, Optional.of(GroupId.v1(group.getGroupId())), 0, content.isNeedsReceipt()); + IncomingTextMessage incoming = new IncomingTextMessage(Recipient.externalPush(context, content.getSender()).getId(), content.getSenderDevice(), content.getTimestamp(), content.getServerTimestamp(), body, Optional.of(GroupId.v1orThrow(group.getGroupId())), 0, content.isNeedsReceipt()); IncomingGroupMessage groupMessage = new IncomingGroupMessage(incoming, storage, body); Optional insertResult = smsDatabase.insertMessageInbox(groupMessage); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberInvitesActivity.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberInvitesActivity.java index 0d547e153a..86b991de2a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberInvitesActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberInvitesActivity.java @@ -37,7 +37,7 @@ public class PendingMemberInvitesActivity extends PassphraseRequiredActionBarAct if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() - .replace(R.id.container, PendingMemberInvitesFragment.newInstance(GroupId.parse(getIntent().getStringExtra(GROUP_ID)).requireV2())) + .replace(R.id.container, PendingMemberInvitesFragment.newInstance(GroupId.parseOrThrow(getIntent().getStringExtra(GROUP_ID)).requireV2())) .commitNow(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberInvitesFragment.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberInvitesFragment.java index f9837cf186..844bba29e2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberInvitesFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/pendingmemberinvites/PendingMemberInvitesFragment.java @@ -80,7 +80,7 @@ public class PendingMemberInvitesFragment extends Fragment { public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); - GroupId.V2 groupId = GroupId.parse(Objects.requireNonNull(requireArguments().getString(GROUP_ID))).requireV2(); + GroupId.V2 groupId = GroupId.parseOrThrow(Objects.requireNonNull(requireArguments().getString(GROUP_ID))).requireV2(); PendingMemberInvitesViewModel.Factory factory = new PendingMemberInvitesViewModel.Factory(requireContext(), groupId); diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java index 09aecf3628..3dd6319ccd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV1DownloadJob.java @@ -113,7 +113,7 @@ public final class AvatarGroupsV1DownloadJob extends BaseJob { public static final class Factory implements Job.Factory { @Override public @NonNull AvatarGroupsV1DownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new AvatarGroupsV1DownloadJob(parameters, GroupId.parse(data.getString(KEY_GROUP_ID)).requireV1()); + return new AvatarGroupsV1DownloadJob(parameters, GroupId.parseOrThrow(data.getString(KEY_GROUP_ID)).requireV1()); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV2DownloadJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV2DownloadJob.java index 6accdfbf4b..98f0e10b81 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV2DownloadJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/AvatarGroupsV2DownloadJob.java @@ -124,7 +124,7 @@ public final class AvatarGroupsV2DownloadJob extends BaseJob { @Override public @NonNull AvatarGroupsV2DownloadJob create(@NonNull Parameters parameters, @NonNull Data data) { return new AvatarGroupsV2DownloadJob(parameters, - GroupId.parse(data.getString(KEY_GROUP_ID)).requireV2(), + GroupId.parseOrThrow(data.getString(KEY_GROUP_ID)).requireV2(), data.getString(CDN_KEY)); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/LeaveGroupJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/LeaveGroupJob.java index 4f91c71b18..b20c9c34ae 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/LeaveGroupJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/LeaveGroupJob.java @@ -166,7 +166,7 @@ public class LeaveGroupJob extends BaseJob { public static class Factory implements Job.Factory { @Override public @NonNull LeaveGroupJob create(@NonNull Parameters parameters, @NonNull Data data) { - return new LeaveGroupJob(GroupId.v1(Base64.decodeOrThrow(data.getString(KEY_GROUP_ID))), + return new LeaveGroupJob(GroupId.v1orThrow(Base64.decodeOrThrow(data.getString(KEY_GROUP_ID))), data.getString(KEY_GROUP_NAME), RecipientId.fromSerializedList(data.getString(KEY_MEMBERS)), RecipientId.fromSerializedList(data.getString(KEY_RECIPIENTS)), diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptMessageJob.java index 72d0ae9fef..b524d64210 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushDecryptMessageJob.java @@ -29,6 +29,8 @@ import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.NoSuchMessageException; import org.thoughtcrime.securesms.database.PushDatabase; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.groups.BadGroupIdException; +import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.jobmanager.Data; import org.thoughtcrime.securesms.jobmanager.Job; import org.thoughtcrime.securesms.jobmanager.JobManager; @@ -226,14 +228,23 @@ public final class PushDecryptMessageJob extends BaseJob { } } - private static PushProcessMessageJob.ExceptionMetadata toExceptionMetadata(@NonNull UnsupportedDataMessageException e) throws NoSenderException { + private static PushProcessMessageJob.ExceptionMetadata toExceptionMetadata(@NonNull UnsupportedDataMessageException e) + throws NoSenderException + { String sender = e.getSender(); if (sender == null) throw new NoSenderException(); + GroupId groupId = null; + try { + groupId = GroupUtil.idFromGroupContext(e.getGroup().orNull()); + } catch (BadGroupIdException ex) { + Log.w(TAG, "Bad group id found in unsupported data message", ex); + } + return new PushProcessMessageJob.ExceptionMetadata(sender, e.getSenderDevice(), - e.getGroup().transform(GroupUtil::idFromGroupContext).orNull()); + groupId); } private static PushProcessMessageJob.ExceptionMetadata toExceptionMetadata(@NonNull ProtocolException e) throws NoSenderException { diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java index 4fb269e551..e7a8d971ce 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushGroupUpdateJob.java @@ -28,7 +28,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceGroup.Type; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.PushNetworkException; -import java.io.ByteArrayInputStream; import java.io.IOException; import java.util.LinkedList; import java.util.List; @@ -140,7 +139,7 @@ public class PushGroupUpdateJob extends BaseJob { public @NonNull PushGroupUpdateJob create(@NonNull Parameters parameters, @NonNull org.thoughtcrime.securesms.jobmanager.Data data) { return new PushGroupUpdateJob(parameters, RecipientId.from(data.getString(KEY_SOURCE)), - GroupId.parse(data.getString(KEY_GROUP_ID))); + GroupId.parseOrThrow(data.getString(KEY_GROUP_ID))); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java index 9ece2d59b8..bcf6f89eea 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushProcessMessageJob.java @@ -43,6 +43,7 @@ import org.thoughtcrime.securesms.database.model.MmsMessageRecord; import org.thoughtcrime.securesms.database.model.ReactionRecord; import org.thoughtcrime.securesms.database.model.StickerRecord; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.groups.BadGroupIdException; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.groups.GroupV1MessageProcessor; import org.thoughtcrime.securesms.jobmanager.Data; @@ -342,6 +343,8 @@ public final class PushProcessMessageJob extends BaseJob { } catch (StorageFailedException e) { Log.w(TAG, e); handleCorruptMessage(e.getSender(), e.getSenderDevice(), timestamp, smsMessageId); + } catch (BadGroupIdException e) { + Log.w(TAG, "Ignoring message with bad group id", e); } } @@ -518,6 +521,7 @@ public final class PushProcessMessageJob extends BaseJob { } private long handleSynchronizeSentEndSessionMessage(@NonNull SentTranscriptMessage message) + throws BadGroupIdException { SmsDatabase database = DatabaseFactory.getSmsDatabase(context); Recipient recipient = getSyncMessageDestination(message); @@ -544,7 +548,7 @@ public final class PushProcessMessageJob extends BaseJob { private void handleGroupV1Message(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message, @NonNull Optional smsMessageId) - throws StorageFailedException + throws StorageFailedException, BadGroupIdException { GroupV1MessageProcessor.process(context, content, message, false); @@ -559,6 +563,7 @@ public final class PushProcessMessageJob extends BaseJob { private void handleUnknownGroupMessage(@NonNull SignalServiceContent content, @NonNull SignalServiceGroupContext group) + throws BadGroupIdException { if (group.getGroupV1().isPresent()) { SignalServiceGroup groupV1 = group.getGroupV1().get(); @@ -575,7 +580,7 @@ public final class PushProcessMessageJob extends BaseJob { private void handleExpirationUpdate(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message, @NonNull Optional smsMessageId) - throws StorageFailedException + throws StorageFailedException, BadGroupIdException { try { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); @@ -717,7 +722,9 @@ public final class PushProcessMessageJob extends BaseJob { } } - private void handleSynchronizeMessageRequestResponse(@NonNull MessageRequestResponseMessage response) { + private void handleSynchronizeMessageRequestResponse(@NonNull MessageRequestResponseMessage response) + throws BadGroupIdException + { RecipientDatabase recipientDatabase = DatabaseFactory.getRecipientDatabase(context); ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(context); @@ -761,8 +768,7 @@ public final class PushProcessMessageJob extends BaseJob { private void handleSynchronizeSentMessage(@NonNull SignalServiceContent content, @NonNull SentTranscriptMessage message) - throws StorageFailedException - + throws StorageFailedException, BadGroupIdException { try { GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); @@ -880,7 +886,7 @@ public final class PushProcessMessageJob extends BaseJob { private void handleMediaMessage(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message, @NonNull Optional smsMessageId) - throws StorageFailedException + throws StorageFailedException, BadGroupIdException { notifyTypingStoppedFromIncomingMessage(getMessageDestination(content, message), content.getSender(), content.getSenderDevice()); @@ -944,7 +950,9 @@ public final class PushProcessMessageJob extends BaseJob { } } - private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) throws MmsException { + private long handleSynchronizeSentExpirationUpdate(@NonNull SentTranscriptMessage message) + throws MmsException, BadGroupIdException + { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); Recipient recipient = getSyncMessageDestination(message); @@ -963,7 +971,7 @@ public final class PushProcessMessageJob extends BaseJob { } private long handleSynchronizeSentMediaMessage(@NonNull SentTranscriptMessage message) - throws MmsException + throws MmsException, BadGroupIdException { MmsDatabase database = DatabaseFactory.getMmsDatabase(context); Recipient recipients = getSyncMessageDestination(message); @@ -1042,7 +1050,9 @@ public final class PushProcessMessageJob extends BaseJob { return threadId; } - private void handleGroupRecipientUpdate(@NonNull SentTranscriptMessage message) { + private void handleGroupRecipientUpdate(@NonNull SentTranscriptMessage message) + throws BadGroupIdException + { Recipient recipient = getSyncMessageDestination(message); if (!recipient.isGroup()) { @@ -1091,7 +1101,7 @@ public final class PushProcessMessageJob extends BaseJob { @NonNull SignalServiceDataMessage message, @NonNull Optional smsMessageId, @NonNull Optional groupId) - throws StorageFailedException + throws StorageFailedException, BadGroupIdException { SmsDatabase database = DatabaseFactory.getSmsDatabase(context); String body = message.getBody().isPresent() ? message.getBody().get() : ""; @@ -1132,7 +1142,7 @@ public final class PushProcessMessageJob extends BaseJob { } private long handleSynchronizeSentTextMessage(@NonNull SentTranscriptMessage message) - throws MmsException + throws MmsException, BadGroupIdException { Recipient recipient = getSyncMessageDestination(message); String body = message.getMessage().getBody().or(""); @@ -1361,6 +1371,7 @@ public final class PushProcessMessageJob extends BaseJob { private void handleTypingMessage(@NonNull SignalServiceContent content, @NonNull SignalServiceTypingMessage typingMessage) + throws BadGroupIdException { if (!TextSecurePreferences.isTypingIndicatorsEnabled(context)) { return; @@ -1554,20 +1565,28 @@ public final class PushProcessMessageJob extends BaseJob { return database.insertMessageInbox(textMessage); } - private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message) { + private Recipient getSyncMessageDestination(@NonNull SentTranscriptMessage message) + throws BadGroupIdException + { return getGroupRecipient(message.getMessage().getGroupContext()) .or(() -> Recipient.externalPush(context, message.getDestination().get())); } private Recipient getMessageDestination(@NonNull SignalServiceContent content, @NonNull SignalServiceDataMessage message) + throws BadGroupIdException { return getGroupRecipient(message.getGroupContext()) .or(() -> Recipient.externalPush(context, content.getSender())); } - private Optional getGroupRecipient(Optional message) { - return message.transform(groupContext -> Recipient.externalGroup(context, GroupUtil.idFromGroupContext(groupContext))); + private Optional getGroupRecipient(Optional message) + throws BadGroupIdException + { + if (message.isPresent()) { + return Optional.of(Recipient.externalGroup(context, GroupUtil.idFromGroupContext(message.get()))); + } + return Optional.absent(); } private void notifyTypingStoppedFromIncomingMessage(@NonNull Recipient conversationRecipient, @NonNull SignalServiceAddress sender, int device) { @@ -1580,7 +1599,9 @@ public final class PushProcessMessageJob extends BaseJob { } } - private boolean shouldIgnore(@Nullable SignalServiceContent content) { + private boolean shouldIgnore(@Nullable SignalServiceContent content) + throws BadGroupIdException + { if (content == null) { Log.w(TAG, "Got a message with null content."); return true; @@ -1596,7 +1617,7 @@ public final class PushProcessMessageJob extends BaseJob { return true; } else if (conversation.isGroup()) { GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context); - Optional groupId = message.getGroupContext().transform(GroupUtil::idFromGroupContext); + Optional groupId = GroupUtil.idFromGroupContext(message.getGroupContext()); if (groupId.isPresent() && groupDatabase.isUnknownGroup(groupId.get())) { return false; @@ -1694,7 +1715,7 @@ public final class PushProcessMessageJob extends BaseJob { } else { ExceptionMetadata exceptionMetadata = new ExceptionMetadata(data.getString(KEY_EXCEPTION_SENDER), data.getInt(KEY_EXCEPTION_DEVICE), - GroupId.parseNullable(data.getStringOrDefault(KEY_EXCEPTION_GROUP_ID, null))); + GroupId.parseNullableOrThrow(data.getStringOrDefault(KEY_EXCEPTION_GROUP_ID, null))); return new PushProcessMessageJob(parameters, state, diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java index 81cf689683..2f281bad19 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RequestGroupInfoJob.java @@ -99,7 +99,7 @@ public class RequestGroupInfoJob extends BaseJob { public @NonNull RequestGroupInfoJob create(@NonNull Parameters parameters, @NonNull Data data) { return new RequestGroupInfoJob(parameters, RecipientId.from(data.getString(KEY_SOURCE)), - GroupId.parse(data.getString(KEY_GROUP_ID))); + GroupId.parseOrThrow(data.getString(KEY_GROUP_ID))); } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java index 07f1b49fe7..59504cf11f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/mms/IncomingMediaMessage.java @@ -92,7 +92,7 @@ public class IncomingMediaMessage { this.quote = quote.orNull(); this.unidentified = unidentified; - if (group.isPresent()) this.groupId = GroupUtil.idFromGroupContext(group.get()); + if (group.isPresent()) this.groupId = GroupUtil.idFromGroupContextOrThrow(group.get()); else this.groupId = null; this.attachments.addAll(PointerAttachment.forPointers(attachments)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java index 3f85d6c533..2305b97dc5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java +++ b/app/src/main/java/org/thoughtcrime/securesms/recipients/Recipient.java @@ -271,7 +271,7 @@ public class Recipient { } } } else if (GroupId.isEncodedGroup(identifier)) { - id = db.getOrInsertFromGroupId(GroupId.parse(identifier)); + id = db.getOrInsertFromGroupId(GroupId.parseOrThrow(identifier)); } else if (NumberUtil.isValidEmail(identifier)) { id = db.getOrInsertFromEmail(identifier); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java index 9d470e2871..5072a27fd3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/sms/IncomingTextMessage.java @@ -96,7 +96,7 @@ public class IncomingTextMessage implements Parcelable { this.pseudoSubject = in.readString(); this.sentTimestampMillis = in.readLong(); this.serverTimestampMillis = in.readLong(); - this.groupId = GroupId.parseNullable(in.readString()); + this.groupId = GroupId.parseNullableOrThrow(in.readString()); this.push = (in.readInt() == 1); this.subscriptionId = in.readInt(); this.expiresInMillis = in.readLong(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV1ConflictMerger.java b/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV1ConflictMerger.java index 6a8e6e662a..de85373539 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV1ConflictMerger.java +++ b/app/src/main/java/org/thoughtcrime/securesms/storage/GroupV1ConflictMerger.java @@ -18,12 +18,12 @@ class GroupV1ConflictMerger implements StorageSyncHelper.ConflictMerger localByGroupId; GroupV1ConflictMerger(@NonNull Collection localOnly) { - localByGroupId = Stream.of(localOnly).collect(Collectors.toMap(g -> GroupId.v1(g.getGroupId()), g -> g)); + localByGroupId = Stream.of(localOnly).collect(Collectors.toMap(g -> GroupId.v1orThrow(g.getGroupId()), g -> g)); } @Override public @NonNull Optional getMatching(@NonNull SignalGroupV1Record record) { - return Optional.fromNullable(localByGroupId.get(GroupId.v1(record.getGroupId()))); + return Optional.fromNullable(localByGroupId.get(GroupId.v1orThrow(record.getGroupId()))); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java index 8650e46d3d..27b7c5ec61 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/GroupUtil.java @@ -11,6 +11,7 @@ import com.google.protobuf.ByteString; import org.thoughtcrime.securesms.R; import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.GroupDatabase; +import org.thoughtcrime.securesms.groups.BadGroupIdException; import org.thoughtcrime.securesms.groups.GroupId; import org.thoughtcrime.securesms.logging.Log; import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage; @@ -38,7 +39,9 @@ public final class GroupUtil { /** * Result may be a v1 or v2 GroupId. */ - public static GroupId idFromGroupContext(@NonNull SignalServiceGroupContext groupContext) { + public static @NonNull GroupId idFromGroupContext(@NonNull SignalServiceGroupContext groupContext) + throws BadGroupIdException + { if (groupContext.getGroupV1().isPresent()) { return GroupId.v1(groupContext.getGroupV1().get().getGroupId()); } else if (groupContext.getGroupV2().isPresent()) { @@ -48,11 +51,24 @@ public final class GroupUtil { } } + public static @NonNull GroupId idFromGroupContextOrThrow(@NonNull SignalServiceGroupContext groupContext) { + try { + return idFromGroupContext(groupContext); + } catch (BadGroupIdException e) { + throw new AssertionError(e); + } + } + /** * Result may be a v1 or v2 GroupId. */ - public static @NonNull Optional idFromGroupContext(@NonNull Optional groupContext) { - return groupContext.transform(GroupUtil::idFromGroupContext); + public static @NonNull Optional idFromGroupContext(@NonNull Optional groupContext) + throws BadGroupIdException + { + if (groupContext.isPresent()) { + return Optional.of(idFromGroupContext(groupContext.get())); + } + return Optional.absent(); } @WorkerThread diff --git a/app/src/test/java/org/thoughtcrime/securesms/groups/GroupIdTest.java b/app/src/test/java/org/thoughtcrime/securesms/groups/GroupIdTest.java index 9448ad5631..1ddc1b8002 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/groups/GroupIdTest.java +++ b/app/src/test/java/org/thoughtcrime/securesms/groups/GroupIdTest.java @@ -23,7 +23,7 @@ public final class GroupIdTest { @Test public void can_create_for_gv1() { - GroupId.V1 groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + GroupId.V1 groupId = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString()); assertFalse(groupId.isMms()); @@ -31,7 +31,7 @@ public final class GroupIdTest { @Test public void can_parse_gv1() { - GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f"); + GroupId groupId = GroupId.parseOrThrow("__textsecure_group__!000102030405060708090a0b0c0d0e0f"); assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString()); assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId()); @@ -67,7 +67,7 @@ public final class GroupIdTest { @Test public void can_parse_gv2() throws IOException { - GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"); + GroupId groupId = GroupId.parseOrThrow("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"); assertEquals("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e", groupId.toString()); assertArrayEquals(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"), groupId.getDecodedId()); @@ -90,7 +90,7 @@ public final class GroupIdTest { @Test public void can_parse_mms() { - GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f"); + GroupId groupId = GroupId.parseOrThrow("__signal_mms_group__!000102030405060708090a0b0c0d0e0f"); assertEquals("__signal_mms_group__!000102030405060708090a0b0c0d0e0f", groupId.toString()); assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId()); @@ -103,14 +103,14 @@ public final class GroupIdTest { @SuppressWarnings("ConstantConditions") @Test public void can_parse_null() { - GroupId groupId = GroupId.parseNullable(null); + GroupId groupId = GroupId.parseNullableOrThrow(null); assertNull(groupId); } @Test public void can_parse_gv1_with_parseNullable() { - GroupId groupId = GroupId.parseNullable("__textsecure_group__!000102030405060708090a0b0c0d0e0f"); + GroupId groupId = GroupId.parseNullableOrThrow("__textsecure_group__!000102030405060708090a0b0c0d0e0f"); assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString()); assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId()); @@ -122,41 +122,41 @@ public final class GroupIdTest { @Test(expected = AssertionError.class) public void bad_encoding__bad_prefix__parseNullable() { - GroupId.parseNullable("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f"); + GroupId.parseNullableOrThrow("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f"); } @Test(expected = AssertionError.class) public void bad_encoding__empty__parseNullable() { - GroupId.parseNullable(""); + GroupId.parseNullableOrThrow(""); } @Test(expected = AssertionError.class) public void bad_encoding__odd_hex__parseNullable() { - GroupId.parseNullable("__textsecure_group__!0001020305060708090bODD_HEX"); + GroupId.parseNullableOrThrow("__textsecure_group__!0001020305060708090bODD_HEX"); } @Test(expected = AssertionError.class) public void bad_encoding__bad_prefix__parse() { - GroupId.parse("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f"); + GroupId.parseOrThrow("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f"); } @Test(expected = AssertionError.class) public void bad_encoding__odd_hex__parse() { - GroupId.parse("__textsecure_group__!0001020305060708090b0c0d0e0fODD_HEX"); + GroupId.parseOrThrow("__textsecure_group__!0001020305060708090b0c0d0e0fODD_HEX"); } @Test public void get_bytes() { byte[] bytes = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }; - GroupId groupId = GroupId.v1(bytes); + GroupId groupId = GroupId.v1orThrow(bytes); assertArrayEquals(bytes, groupId.getDecodedId()); } @Test public void equality() { - GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); - GroupId groupId2 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + GroupId groupId1 = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + GroupId groupId2 = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); assertNotSame(groupId1, groupId2); assertEquals(groupId1, groupId2); @@ -165,8 +165,8 @@ public final class GroupIdTest { @Test public void inequality_by_bytes() { - GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); - GroupId groupId2 = GroupId.v1(new byte[]{ 0, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + GroupId groupId1 = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + GroupId groupId2 = GroupId.v1orThrow(new byte[]{ 0, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); assertNotSame(groupId1, groupId2); assertNotEquals(groupId1, groupId2); @@ -175,7 +175,7 @@ public final class GroupIdTest { @Test public void inequality_of_sms_and_mms() { - GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + GroupId groupId1 = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); GroupId groupId2 = GroupId.mms(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); assertNotSame(groupId1, groupId2); @@ -185,14 +185,14 @@ public final class GroupIdTest { @Test public void inequality_with_null() { - GroupId groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); + GroupId groupId = GroupId.v1orThrow(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }); assertNotEquals(groupId, null); } @Test public void require_mms() { - GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f"); + GroupId groupId = GroupId.parseOrThrow("__signal_mms_group__!000102030405060708090a0b0c0d0e0f"); GroupId.Mms mms = groupId.requireMms(); @@ -201,7 +201,7 @@ public final class GroupIdTest { @Test public void require_v1_and_push() { - GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f"); + GroupId groupId = GroupId.parseOrThrow("__textsecure_group__!000102030405060708090a0b0c0d0e0f"); GroupId.V1 v1 = groupId.requireV1(); GroupId.Push push = groupId.requirePush(); @@ -212,9 +212,9 @@ public final class GroupIdTest { @Test public void require_v2_and_push() { - GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"); + GroupId groupId = GroupId.parseOrThrow("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"); - GroupId.V2 v2 = groupId.requireV2 (); + GroupId.V2 v2 = groupId.requireV2(); GroupId.Push push = groupId.requirePush(); assertSame(groupId, v2); @@ -222,35 +222,35 @@ public final class GroupIdTest { } @Test(expected = AssertionError.class) - public void cannot_require_push_of_mms() { + public void cannot_require_push_of_mms() throws BadGroupIdException { GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f"); groupId.requirePush(); } @Test(expected = AssertionError.class) - public void cannot_require_v1_of_mms() { + public void cannot_require_v1_of_mms() throws BadGroupIdException { GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f"); groupId.requireV1(); } @Test(expected = AssertionError.class) - public void cannot_require_v2_of_mms() { + public void cannot_require_v2_of_mms() throws BadGroupIdException { GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f"); groupId.requireV2(); } @Test(expected = AssertionError.class) - public void cannot_require_v1_of_v2() { + public void cannot_require_v1_of_v2() throws BadGroupIdException { GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"); groupId.requireV1(); } @Test(expected = AssertionError.class) - public void cannot_require_v2_of_v1() { + public void cannot_require_v2_of_v1() throws BadGroupIdException { GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f"); groupId.requireV2(); @@ -258,12 +258,17 @@ public final class GroupIdTest { @Test(expected = AssertionError.class) public void cannot_create_v1_with_a_v2_length() throws IOException { - GroupId.v1(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e")); + GroupId.v1orThrow(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e")); + } + + @Test(expected = BadGroupIdException.class) + public void cannot_create_v2_with_a_v1_length() throws IOException, BadGroupIdException { + GroupId.v2(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f")); } @Test(expected = AssertionError.class) - public void cannot_create_v2_with_a_v1_length() throws IOException { - GroupId.v2(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f")); + public void cannot_create_v2_with_a_v1_length_assert() throws IOException { + GroupId.v2orThrow(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f")); } @Test @@ -283,14 +288,14 @@ public final class GroupIdTest { } @Test - public void parse_bytes_to_v1_via_push() { + public void parse_bytes_to_v1_via_push() throws BadGroupIdException { GroupId.V1 v1 = GroupId.push(new byte[]{ 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8 }).requireV1(); assertEquals("__textsecure_group__!090a0b0c0d0e0f000102030405060708", v1.toString()); } @Test - public void parse_bytes_to_v2_via_by_push() { + public void parse_bytes_to_v2_via_by_push() throws BadGroupIdException { GroupId.V2 v2 = GroupId.push(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }).requireV2(); assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f", v2.toString());