Validate incoming Group lengths and remote delete entries if wrong.

Ignore incoming messages with bad V1 group lengths.
This commit is contained in:
Alan Evans
2020-09-10 14:39:29 -03:00
committed by GitHub
parent bf4cac0c82
commit 3cffaddc0a
11 changed files with 218 additions and 77 deletions

View File

@@ -261,19 +261,19 @@ public final class GroupIdTest {
groupId.requireV2();
}
@Test(expected = BadGroupIdException.class)
public void cannot_create_v1_with_a_v2_length() throws IOException, BadGroupIdException {
GroupId.v1(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"));
}
@Test(expected = AssertionError.class)
public void cannot_create_v1_with_a_v2_length() throws IOException {
public void cannot_create_v1_with_a_v2_length_assert() throws IOException {
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_assert() throws IOException {
GroupId.v2orThrow(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f"));
public void cannot_create_v1_with_wrong_length() throws IOException, BadGroupIdException {
GroupId.v1Exact(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e"));
}
@Test
@@ -292,6 +292,22 @@ public final class GroupIdTest {
assertTrue(v1.isV1());
}
@Test
public void v1_static_factory() throws BadGroupIdException {
GroupId.V1 v1 = GroupId.v1(new byte[]{ 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8 });
assertEquals("__textsecure_group__!090a0b0c0d0e0f000102030405060708", v1.toString());
assertTrue(v1.isV1());
}
@Test
public void v1Exact_static_factory() throws BadGroupIdException {
GroupId.V1 v1 = GroupId.v1Exact(new byte[]{ 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8 });
assertEquals("__textsecure_group__!090a0b0c0d0e0f000102030405060708", v1.toString());
assertTrue(v1.isV1());
}
@Test
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();

View File

@@ -1,10 +1,11 @@
package org.thoughtcrime.securesms.storage;
import org.junit.Test;
import org.thoughtcrime.securesms.storage.GroupV1ConflictMerger;
import org.thoughtcrime.securesms.storage.StorageSyncHelper.KeyGenerator;
import org.whispersystems.signalservice.api.storage.SignalGroupV1Record;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import static org.junit.Assert.assertArrayEquals;
@@ -14,10 +15,11 @@ import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.thoughtcrime.securesms.testutil.TestHelpers.byteArray;
public class GroupV1ConflictMergerTest {
public final class GroupV1ConflictMergerTest {
private static final byte[] GENERATED_KEY = byteArray(8675309);
private static final KeyGenerator KEY_GENERATOR = mock(KeyGenerator.class);
private static byte[] GENERATED_KEY = byteArray(8675309);
private static KeyGenerator KEY_GENERATOR = mock(KeyGenerator.class);
static {
when(KEY_GENERATOR.generate()).thenReturn(GENERATED_KEY);
}
@@ -78,4 +80,31 @@ public class GroupV1ConflictMergerTest {
assertEquals(local, merged);
}
@Test
public void merge_excludeBadGroupId() {
SignalGroupV1Record badRemote = new SignalGroupV1Record.Builder(byteArray(1), badGroupKey(99))
.setBlocked(false)
.setProfileSharingEnabled(true)
.setArchived(true)
.build();
SignalGroupV1Record goodRemote = new SignalGroupV1Record.Builder(byteArray(1), groupKey(99))
.setBlocked(false)
.setProfileSharingEnabled(true)
.setArchived(true)
.build();
Collection<SignalGroupV1Record> invalid = new GroupV1ConflictMerger(Collections.emptyList()).getInvalidEntries(Arrays.asList(badRemote, goodRemote));
assertEquals(Collections.singletonList(badRemote), invalid);
}
private static byte[] groupKey(int value) {
return byteArray(value, 16);
}
private static byte[] badGroupKey(int value) {
return byteArray(value, 32);
}
}

View File

@@ -1,11 +1,11 @@
package org.thoughtcrime.securesms.storage;
import org.junit.Test;
import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.groups.GroupMasterKey;
import org.thoughtcrime.securesms.storage.StorageSyncHelper.KeyGenerator;
import org.whispersystems.signalservice.api.storage.SignalGroupV2Record;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import static org.junit.Assert.assertArrayEquals;
@@ -15,10 +15,11 @@ import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.when;
import static org.thoughtcrime.securesms.testutil.TestHelpers.byteArray;
public class GroupV2ConflictMergerTest {
public final class GroupV2ConflictMergerTest {
private static final byte[] GENERATED_KEY = byteArray(8675309);
private static final KeyGenerator KEY_GENERATOR = mock(KeyGenerator.class);
private static byte[] GENERATED_KEY = byteArray(8675309);
private static KeyGenerator KEY_GENERATOR = mock(KeyGenerator.class);
static {
when(KEY_GENERATOR.generate()).thenReturn(GENERATED_KEY);
}
@@ -26,20 +27,20 @@ public class GroupV2ConflictMergerTest {
@Test
public void merge_alwaysPreferRemote_exceptProfileSharingIsEitherOr() {
SignalGroupV2Record remote = new SignalGroupV2Record.Builder(byteArray(1), groupKey(100))
.setBlocked(false)
.setProfileSharingEnabled(false)
.setArchived(false)
.build();
.setBlocked(false)
.setProfileSharingEnabled(false)
.setArchived(false)
.build();
SignalGroupV2Record local = new SignalGroupV2Record.Builder(byteArray(2), groupKey(100))
.setBlocked(true)
.setProfileSharingEnabled(true)
.setArchived(true)
.build();
.setBlocked(true)
.setProfileSharingEnabled(true)
.setArchived(true)
.build();
SignalGroupV2Record merged = new GroupV2ConflictMerger(Collections.singletonList(local)).merge(remote, local, KEY_GENERATOR);
assertArrayEquals(GENERATED_KEY, merged.getId().getRaw());
assertEquals(groupKey(100), merged.getMasterKey());
assertArrayEquals(groupKey(100), merged.getMasterKeyBytes());
assertFalse(merged.isBlocked());
assertFalse(merged.isArchived());
}
@@ -80,11 +81,30 @@ public class GroupV2ConflictMergerTest {
assertEquals(local, merged);
}
private static GroupMasterKey groupKey(int value) {
try {
return new GroupMasterKey(byteArray(value, 32));
} catch (InvalidInputException e) {
throw new AssertionError(e);
}
@Test
public void merge_excludeBadGroupId() {
SignalGroupV2Record badRemote = new SignalGroupV2Record.Builder(byteArray(1), badGroupKey(99))
.setBlocked(false)
.setProfileSharingEnabled(true)
.setArchived(true)
.build();
SignalGroupV2Record goodRemote = new SignalGroupV2Record.Builder(byteArray(1), groupKey(99))
.setBlocked(false)
.setProfileSharingEnabled(true)
.setArchived(true)
.build();
Collection<SignalGroupV2Record> invalid = new GroupV2ConflictMerger(Collections.emptyList()).getInvalidEntries(Arrays.asList(badRemote, goodRemote));
assertEquals(Collections.singletonList(badRemote), invalid);
}
private static byte[] groupKey(int value) {
return byteArray(value, 32);
}
private static byte[] badGroupKey(int value) {
return byteArray(value, 16);
}
}

View File

@@ -129,6 +129,34 @@ public final class StorageSyncHelperTest {
assertEquals(setOf(remote1), result.getRemoteDeletes());
}
@Test
public void resolveConflict_contact_deleteBadGv1() {
SignalGroupV1Record remote1 = badGroupV1(1, 1, true, false);
SignalGroupV1Record local1 = groupV1(2, 1, true, true);
MergeResult result = StorageSyncHelper.resolveConflict(recordSetOf(remote1), recordSetOf(local1));
assertTrue(result.getLocalContactInserts().isEmpty());
assertTrue(result.getLocalContactUpdates().isEmpty());
assertEquals(setOf(record(local1)), result.getRemoteInserts());
assertTrue(result.getRemoteUpdates().isEmpty());
assertEquals(setOf(remote1), result.getRemoteDeletes());
}
@Test
public void resolveConflict_contact_deleteBadGv2() {
SignalGroupV2Record remote1 = badGroupV2(1, 2, true, false);
SignalGroupV2Record local1 = groupV2(2, 2, true, false);
MergeResult result = StorageSyncHelper.resolveConflict(recordSetOf(remote1), recordSetOf(local1));
assertTrue(result.getLocalContactInserts().isEmpty());
assertTrue(result.getLocalContactUpdates().isEmpty());
assertEquals(setOf(record(local1)), result.getRemoteInserts());
assertTrue(result.getRemoteUpdates().isEmpty());
assertEquals(setOf(remote1), result.getRemoteDeletes());
}
@Test
public void resolveConflict_contact_sameAsRemote() {
SignalContactRecord remote1 = contact(1, UUID_A, E164_A, "a");
@@ -417,7 +445,15 @@ public final class StorageSyncHelperTest {
boolean blocked,
boolean profileSharing)
{
return new SignalGroupV1Record.Builder(byteArray(key), byteArray(groupId)).setBlocked(blocked).setProfileSharingEnabled(profileSharing).build();
return new SignalGroupV1Record.Builder(byteArray(key), byteArray(groupId, 16)).setBlocked(blocked).setProfileSharingEnabled(profileSharing).build();
}
private static SignalGroupV1Record badGroupV1(int key,
int groupId,
boolean blocked,
boolean profileSharing)
{
return new SignalGroupV1Record.Builder(byteArray(key), byteArray(groupId, 42)).setBlocked(blocked).setProfileSharingEnabled(profileSharing).build();
}
private static SignalGroupV2Record groupV2(int key,
@@ -425,11 +461,15 @@ public final class StorageSyncHelperTest {
boolean blocked,
boolean profileSharing)
{
try {
return new SignalGroupV2Record.Builder(byteArray(key), new GroupMasterKey(byteArray(groupId, 32))).setBlocked(blocked).setProfileSharingEnabled(profileSharing).build();
} catch (InvalidInputException e) {
throw new AssertionError(e);
}
return new SignalGroupV2Record.Builder(byteArray(key), byteArray(groupId, 32)).setBlocked(blocked).setProfileSharingEnabled(profileSharing).build();
}
private static SignalGroupV2Record badGroupV2(int key,
int groupId,
boolean blocked,
boolean profileSharing)
{
return new SignalGroupV2Record.Builder(byteArray(key), byteArray(groupId, 42)).setBlocked(blocked).setProfileSharingEnabled(profileSharing).build();
}
private static <E extends SignalRecord> StorageSyncHelper.RecordUpdate<E> update(E oldRecord, E newRecord) {