GroupId for GV2.
This commit is contained in:
parent
d8fa46c558
commit
66c7f8bcb2
10 changed files with 358 additions and 62 deletions
|
@ -26,7 +26,6 @@ import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPoin
|
||||||
|
|
||||||
import java.io.Closeable;
|
import java.io.Closeable;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -233,7 +232,7 @@ public class GroupDatabase extends Database {
|
||||||
contentValues.put(AVATAR_RELAY, relay);
|
contentValues.put(AVATAR_RELAY, relay);
|
||||||
contentValues.put(TIMESTAMP, System.currentTimeMillis());
|
contentValues.put(TIMESTAMP, System.currentTimeMillis());
|
||||||
contentValues.put(ACTIVE, 1);
|
contentValues.put(ACTIVE, 1);
|
||||||
contentValues.put(MMS, groupId.isMmsGroup());
|
contentValues.put(MMS, groupId.isMms());
|
||||||
|
|
||||||
databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
|
databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
|
||||||
|
|
||||||
|
|
|
@ -356,7 +356,7 @@ public class RecipientDatabase extends Database {
|
||||||
if (result.neededInsert) {
|
if (result.neededInsert) {
|
||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
|
|
||||||
if (groupId.isMmsGroup()) {
|
if (groupId.isMms()) {
|
||||||
values.put(GROUP_TYPE, GroupType.MMS.getId());
|
values.put(GROUP_TYPE, GroupType.MMS.getId());
|
||||||
} else {
|
} else {
|
||||||
values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId());
|
values.put(GROUP_TYPE, GroupType.SIGNAL_V1.getId());
|
||||||
|
@ -1406,9 +1406,9 @@ public class RecipientDatabase extends Database {
|
||||||
db.update(TABLE_NAME, setBlocked, UUID + " = ?", new String[] { uuid });
|
db.update(TABLE_NAME, setBlocked, UUID + " = ?", new String[] { uuid });
|
||||||
}
|
}
|
||||||
|
|
||||||
List<GroupId> groupIdStrings = Stream.of(groupIds).map(GroupId::v1).toList();
|
List<GroupId.V1> groupIdStrings = Stream.of(groupIds).map(GroupId::v1).toList();
|
||||||
|
|
||||||
for (GroupId groupId : groupIdStrings) {
|
for (GroupId.V1 groupId : groupIdStrings) {
|
||||||
db.update(TABLE_NAME, setBlocked, GROUP_ID + " = ?", new String[] { groupId.toString() });
|
db.update(TABLE_NAME, setBlocked, GROUP_ID + " = ?", new String[] { groupId.toString() });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,27 +3,52 @@ package org.thoughtcrime.securesms.groups;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.signal.zkgroup.groups.GroupIdentifier;
|
||||||
|
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||||
|
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||||
import org.thoughtcrime.securesms.util.Hex;
|
import org.thoughtcrime.securesms.util.Hex;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public final class GroupId {
|
public abstract class GroupId {
|
||||||
|
|
||||||
private static final String ENCODED_SIGNAL_GROUP_PREFIX = "__textsecure_group__!";
|
private static final String ENCODED_SIGNAL_GROUP_PREFIX = "__textsecure_group__!";
|
||||||
private static final String ENCODED_MMS_GROUP_PREFIX = "__signal_mms_group__!";
|
private static final String ENCODED_MMS_GROUP_PREFIX = "__signal_mms_group__!";
|
||||||
|
private static final int V2_BYTE_LENGTH = GroupIdentifier.SIZE;
|
||||||
|
private static final int V2_ENCODED_LENGTH = ENCODED_SIGNAL_GROUP_PREFIX.length() + V2_BYTE_LENGTH * 2;
|
||||||
|
|
||||||
private final String encodedId;
|
private final String encodedId;
|
||||||
|
|
||||||
private GroupId(@NonNull String encodedId) {
|
private GroupId(@NonNull String prefix, @NonNull byte[] bytes) {
|
||||||
this.encodedId = encodedId;
|
this.encodedId = prefix + Hex.toStringCondensed(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull GroupId v1(byte[] gv1GroupIdBytes) {
|
public static @NonNull GroupId.Mms mms(byte[] mmsGroupIdBytes) {
|
||||||
return new GroupId(ENCODED_SIGNAL_GROUP_PREFIX + Hex.toStringCondensed(gv1GroupIdBytes));
|
return new GroupId.Mms(mmsGroupIdBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull GroupId mms(byte[] mmsGroupIdBytes) {
|
public static @NonNull GroupId.V1 v1(byte[] gv1GroupIdBytes) {
|
||||||
return new GroupId(ENCODED_MMS_GROUP_PREFIX + Hex.toStringCondensed(mmsGroupIdBytes));
|
if (gv1GroupIdBytes.length == V2_BYTE_LENGTH) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
return new GroupId.V1(gv1GroupIdBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupId.V2 v2(@NonNull byte[] bytes) {
|
||||||
|
if (bytes.length != V2_BYTE_LENGTH) {
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
return new GroupId.V2(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupId.V2 v2(@NonNull GroupIdentifier groupIdentifier) {
|
||||||
|
return v2(groupIdentifier.serialize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupId.V2 v2(@NonNull GroupMasterKey masterKey) {
|
||||||
|
return v2(GroupSecretParams.deriveFromMasterKey(masterKey)
|
||||||
|
.getPublicParams()
|
||||||
|
.getGroupIdentifier());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @NonNull GroupId parse(@NonNull String encodedGroupId) {
|
public static @NonNull GroupId parse(@NonNull String encodedGroupId) {
|
||||||
|
@ -33,7 +58,11 @@ public final class GroupId {
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] bytes = extractDecodedId(encodedGroupId);
|
byte[] bytes = extractDecodedId(encodedGroupId);
|
||||||
return isMmsGroup(encodedGroupId) ? mms(bytes) : v1(bytes);
|
|
||||||
|
if (encodedGroupId.startsWith(ENCODED_MMS_GROUP_PREFIX)) return mms(bytes);
|
||||||
|
else if (encodedGroupId.length() == V2_ENCODED_LENGTH) return v2(bytes);
|
||||||
|
else return v1(bytes);
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
@ -55,10 +84,6 @@ public final class GroupId {
|
||||||
return Hex.fromStringCondensed(encodedGroupId.split("!", 2)[1]);
|
return Hex.fromStringCondensed(encodedGroupId.split("!", 2)[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isMmsGroup(@NonNull String groupId) {
|
|
||||||
return groupId.startsWith(ENCODED_MMS_GROUP_PREFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getDecodedId() {
|
public byte[] getDecodedId() {
|
||||||
try {
|
try {
|
||||||
return extractDecodedId(encodedId);
|
return extractDecodedId(encodedId);
|
||||||
|
@ -67,10 +92,6 @@ public final class GroupId {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMmsGroup() {
|
|
||||||
return isMmsGroup(encodedId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(@Nullable Object obj) {
|
public boolean equals(@Nullable Object obj) {
|
||||||
if (obj instanceof GroupId) {
|
if (obj instanceof GroupId) {
|
||||||
|
@ -90,4 +111,109 @@ public final class GroupId {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return encodedId;
|
return encodedId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract boolean isMms();
|
||||||
|
|
||||||
|
public abstract boolean isV1();
|
||||||
|
|
||||||
|
public abstract boolean isV2();
|
||||||
|
|
||||||
|
public abstract boolean isPush();
|
||||||
|
|
||||||
|
public GroupId.Mms requireMms() {
|
||||||
|
if (this instanceof GroupId.Mms) return (GroupId.Mms) this;
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupId.V1 requireV1() {
|
||||||
|
if (this instanceof GroupId.V1) return (GroupId.V1) this;
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupId.V2 requireV2() {
|
||||||
|
if (this instanceof GroupId.V2) return (GroupId.V2) this;
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupId.Push requirePush() {
|
||||||
|
if (this instanceof GroupId.Push) return (GroupId.Push) this;
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class Mms extends GroupId {
|
||||||
|
|
||||||
|
private Mms(@NonNull byte[] bytes) {
|
||||||
|
super(ENCODED_MMS_GROUP_PREFIX, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMms() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isV1() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isV2() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPush() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static abstract class Push extends GroupId {
|
||||||
|
private Push(@NonNull byte[] bytes) {
|
||||||
|
super(ENCODED_SIGNAL_GROUP_PREFIX, bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isMms() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isPush() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class V1 extends GroupId.Push {
|
||||||
|
|
||||||
|
private V1(@NonNull byte[] bytes) {
|
||||||
|
super(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isV1() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isV2() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final class V2 extends GroupId.Push {
|
||||||
|
|
||||||
|
private V2(@NonNull byte[] bytes) {
|
||||||
|
super(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isV1() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isV2() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,7 @@ public final class GroupManager {
|
||||||
public static boolean leaveGroup(@NonNull Context context, @NonNull Recipient groupRecipient) {
|
public static boolean leaveGroup(@NonNull Context context, @NonNull Recipient groupRecipient) {
|
||||||
GroupId groupId = groupRecipient.requireGroupId();
|
GroupId groupId = groupRecipient.requireGroupId();
|
||||||
|
|
||||||
return V1GroupManager.leaveGroup(context, groupId, groupRecipient);
|
return V1GroupManager.leaveGroup(context, groupId.requireV1(), groupRecipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GroupActionResult {
|
public static class GroupActionResult {
|
||||||
|
|
|
@ -68,7 +68,7 @@ final class V1GroupManager {
|
||||||
}
|
}
|
||||||
groupDatabase.onAvatarUpdated(groupId, avatarBytes != null);
|
groupDatabase.onAvatarUpdated(groupId, avatarBytes != null);
|
||||||
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient.getId(), true);
|
DatabaseFactory.getRecipientDatabase(context).setProfileSharing(groupRecipient.getId(), true);
|
||||||
return sendGroupUpdate(context, groupId, memberIds, name, avatarBytes);
|
return sendGroupUpdate(context, groupId.requireV1(), memberIds, name, avatarBytes);
|
||||||
} else {
|
} else {
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||||
return new GroupActionResult(groupRecipient, threadId);
|
return new GroupActionResult(groupRecipient, threadId);
|
||||||
|
@ -91,13 +91,13 @@ final class V1GroupManager {
|
||||||
groupDatabase.updateTitle(groupId, name);
|
groupDatabase.updateTitle(groupId, name);
|
||||||
groupDatabase.onAvatarUpdated(groupId, avatarBytes != null);
|
groupDatabase.onAvatarUpdated(groupId, avatarBytes != null);
|
||||||
|
|
||||||
if (!groupId.isMmsGroup()) {
|
if (groupId.isPush()) {
|
||||||
try {
|
try {
|
||||||
AvatarHelper.setAvatar(context, groupRecipientId, avatarBytes != null ? new ByteArrayInputStream(avatarBytes) : null);
|
AvatarHelper.setAvatar(context, groupRecipientId, avatarBytes != null ? new ByteArrayInputStream(avatarBytes) : null);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.w(TAG, "Failed to save avatar!", e);
|
Log.w(TAG, "Failed to save avatar!", e);
|
||||||
}
|
}
|
||||||
return sendGroupUpdate(context, groupId, memberAddresses, name, avatarBytes);
|
return sendGroupUpdate(context, groupId.requireV1(), memberAddresses, name, avatarBytes);
|
||||||
} else {
|
} else {
|
||||||
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
Recipient groupRecipient = Recipient.resolved(groupRecipientId);
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
||||||
|
@ -106,7 +106,7 @@ final class V1GroupManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
|
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
|
||||||
@NonNull GroupId groupId,
|
@NonNull GroupId.V1 groupId,
|
||||||
@NonNull Set<RecipientId> members,
|
@NonNull Set<RecipientId> members,
|
||||||
@Nullable String groupName,
|
@Nullable String groupName,
|
||||||
@Nullable byte[] avatar)
|
@Nullable byte[] avatar)
|
||||||
|
@ -143,7 +143,7 @@ final class V1GroupManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
static boolean leaveGroup(@NonNull Context context, @NonNull GroupId groupId, @NonNull Recipient groupRecipient) {
|
static boolean leaveGroup(@NonNull Context context, @NonNull GroupId.V1 groupId, @NonNull Recipient groupRecipient) {
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(groupRecipient);
|
||||||
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);
|
Optional<OutgoingGroupMediaMessage> leaveMessage = GroupUtil.createGroupLeaveMessage(context, groupRecipient);
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ public class LeaveGroupJob extends BaseJob {
|
||||||
private static final String KEY_MEMBERS = "members";
|
private static final String KEY_MEMBERS = "members";
|
||||||
private static final String KEY_RECIPIENTS = "recipients";
|
private static final String KEY_RECIPIENTS = "recipients";
|
||||||
|
|
||||||
private final GroupId groupId;
|
private final GroupId.Push groupId;
|
||||||
private final String name;
|
private final String name;
|
||||||
private final List<RecipientId> members;
|
private final List<RecipientId> members;
|
||||||
private final List<RecipientId> recipients;
|
private final List<RecipientId> recipients;
|
||||||
|
@ -60,7 +60,7 @@ public class LeaveGroupJob extends BaseJob {
|
||||||
List<RecipientId> members = Stream.of(group.resolve().getParticipants()).map(Recipient::getId).toList();
|
List<RecipientId> members = Stream.of(group.resolve().getParticipants()).map(Recipient::getId).toList();
|
||||||
members.remove(Recipient.self().getId());
|
members.remove(Recipient.self().getId());
|
||||||
|
|
||||||
return new LeaveGroupJob(group.getGroupId().get(),
|
return new LeaveGroupJob(group.getGroupId().get().requirePush(),
|
||||||
group.resolve().getDisplayName(ApplicationDependencies.getApplication()),
|
group.resolve().getDisplayName(ApplicationDependencies.getApplication()),
|
||||||
members,
|
members,
|
||||||
members,
|
members,
|
||||||
|
@ -72,7 +72,7 @@ public class LeaveGroupJob extends BaseJob {
|
||||||
.build());
|
.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
private LeaveGroupJob(@NonNull GroupId groupId,
|
private LeaveGroupJob(@NonNull GroupId.Push groupId,
|
||||||
@NonNull String name,
|
@NonNull String name,
|
||||||
@NonNull List<RecipientId> members,
|
@NonNull List<RecipientId> members,
|
||||||
@NonNull List<RecipientId> recipients,
|
@NonNull List<RecipientId> recipients,
|
||||||
|
|
|
@ -197,7 +197,7 @@ public final class LiveRecipient {
|
||||||
List<Recipient> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).map(this::fetchRecipientFromDisk).toList();
|
List<Recipient> members = Stream.of(groupRecord.get().getMembers()).filterNot(RecipientId::isUnknown).map(this::fetchRecipientFromDisk).toList();
|
||||||
Optional<Long> avatarId = Optional.absent();
|
Optional<Long> avatarId = Optional.absent();
|
||||||
|
|
||||||
if (settings.getGroupId() != null && !settings.getGroupId().isMmsGroup() && title == null) {
|
if (settings.getGroupId() != null && settings.getGroupId().isPush() && title == null) {
|
||||||
title = unnamedGroupName;
|
title = unnamedGroupName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -381,7 +381,7 @@ public class Recipient {
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable String getName(@NonNull Context context) {
|
public @Nullable String getName(@NonNull Context context) {
|
||||||
if (this.name == null && groupId != null && groupId.isMmsGroup()) {
|
if (this.name == null && groupId != null && groupId.isMms()) {
|
||||||
List<String> names = new LinkedList<>();
|
List<String> names = new LinkedList<>();
|
||||||
|
|
||||||
for (Recipient recipient : participants) {
|
for (Recipient recipient : participants) {
|
||||||
|
@ -567,12 +567,12 @@ public class Recipient {
|
||||||
|
|
||||||
public boolean isMmsGroup() {
|
public boolean isMmsGroup() {
|
||||||
GroupId groupId = resolve().groupId;
|
GroupId groupId = resolve().groupId;
|
||||||
return groupId != null && groupId.isMmsGroup();
|
return groupId != null && groupId.isMms();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPushGroup() {
|
public boolean isPushGroup() {
|
||||||
GroupId groupId = resolve().groupId;
|
GroupId groupId = resolve().groupId;
|
||||||
return groupId != null && !groupId.isMmsGroup();
|
return groupId != null && groupId.isPush();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @NonNull List<Recipient> getParticipants() {
|
public @NonNull List<Recipient> getParticipants() {
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
package org.thoughtcrime.securesms.groups;
|
package org.thoughtcrime.securesms.groups;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.signal.zkgroup.InvalidInputException;
|
||||||
|
import org.signal.zkgroup.groups.GroupIdentifier;
|
||||||
|
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||||
|
import org.thoughtcrime.securesms.util.Hex;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
@ -8,42 +14,89 @@ import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotEquals;
|
import static org.junit.Assert.assertNotEquals;
|
||||||
import static org.junit.Assert.assertNotSame;
|
import static org.junit.Assert.assertNotSame;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
import static org.thoughtcrime.securesms.groups.ZkGroupLibraryUtil.assumeZkGroupSupportedOnOS;
|
||||||
|
|
||||||
public final class GroupIdTest {
|
public final class GroupIdTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void can_create_for_gv1() {
|
public void can_create_for_gv1() {
|
||||||
GroupId groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
|
GroupId.V1 groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
|
||||||
|
|
||||||
assertEquals("__textsecure_group__!0001020305060708090b0c0d0e0f", groupId.toString());
|
assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
|
||||||
assertFalse(groupId.isMmsGroup());
|
assertFalse(groupId.isMms());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void can_parse_gv1() {
|
public void can_parse_gv1() {
|
||||||
GroupId groupId = GroupId.parse("__textsecure_group__!0001020305060708090b0c0d0e0f");
|
GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
assertEquals("__textsecure_group__!0001020305060708090b0c0d0e0f", groupId.toString());
|
assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
|
||||||
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
|
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
|
||||||
assertFalse(groupId.isMmsGroup());
|
assertFalse(groupId.isMms());
|
||||||
|
assertTrue(groupId.isV1());
|
||||||
|
assertFalse(groupId.isV2());
|
||||||
|
assertTrue(groupId.isPush());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void can_create_for_gv2_from_GroupIdentifier() throws IOException, InvalidInputException {
|
||||||
|
GroupId.V2 groupId = GroupId.v2(new GroupIdentifier(Hex.fromStringCondensed("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")));
|
||||||
|
|
||||||
|
assertEquals("__textsecure_group__!0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef", groupId.toString());
|
||||||
|
assertFalse(groupId.isMms());
|
||||||
|
assertFalse(groupId.isV1());
|
||||||
|
assertTrue(groupId.isV2());
|
||||||
|
assertTrue(groupId.isPush());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void can_create_for_gv2_from_GroupMasterKey() throws IOException, InvalidInputException {
|
||||||
|
assumeZkGroupSupportedOnOS();
|
||||||
|
|
||||||
|
GroupId.V2 groupId = GroupId.v2(new GroupMasterKey(Hex.fromStringCondensed("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")));
|
||||||
|
|
||||||
|
assertEquals("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e", groupId.toString());
|
||||||
|
assertFalse(groupId.isMms());
|
||||||
|
assertFalse(groupId.isV1());
|
||||||
|
assertTrue(groupId.isV2());
|
||||||
|
assertTrue(groupId.isPush());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void can_parse_gv2() throws IOException {
|
||||||
|
GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e");
|
||||||
|
|
||||||
|
assertEquals("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e", groupId.toString());
|
||||||
|
assertArrayEquals(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"), groupId.getDecodedId());
|
||||||
|
assertFalse(groupId.isMms());
|
||||||
|
assertFalse(groupId.isV1());
|
||||||
|
assertTrue(groupId.isV2());
|
||||||
|
assertTrue(groupId.isPush());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void can_create_for_mms() {
|
public void can_create_for_mms() {
|
||||||
GroupId groupId = GroupId.mms(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
|
GroupId.Mms groupId = GroupId.mms(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
|
||||||
|
|
||||||
assertEquals("__signal_mms_group__!0001020305060708090b0c0d0e0f", groupId.toString());
|
assertEquals("__signal_mms_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
|
||||||
assertTrue(groupId.isMmsGroup());
|
assertTrue(groupId.isMms());
|
||||||
|
assertFalse(groupId.isV1());
|
||||||
|
assertFalse(groupId.isV2());
|
||||||
|
assertFalse(groupId.isPush());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void can_parse_mms() {
|
public void can_parse_mms() {
|
||||||
GroupId groupId = GroupId.parse("__signal_mms_group__!0001020305060708090b0c0d0e0f");
|
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
assertEquals("__signal_mms_group__!0001020305060708090b0c0d0e0f", groupId.toString());
|
assertEquals("__signal_mms_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
|
||||||
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
|
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
|
||||||
assertTrue(groupId.isMmsGroup());
|
assertTrue(groupId.isMms());
|
||||||
|
assertFalse(groupId.isV1());
|
||||||
|
assertFalse(groupId.isV2());
|
||||||
|
assertFalse(groupId.isPush());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
@ -56,16 +109,19 @@ public final class GroupIdTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void can_parse_gv1_with_parseNullable() {
|
public void can_parse_gv1_with_parseNullable() {
|
||||||
GroupId groupId = GroupId.parseNullable("__textsecure_group__!0001020305060708090b0c0d0e0f");
|
GroupId groupId = GroupId.parseNullable("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
assertEquals("__textsecure_group__!0001020305060708090b0c0d0e0f", groupId.toString());
|
assertEquals("__textsecure_group__!000102030405060708090a0b0c0d0e0f", groupId.toString());
|
||||||
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
|
assertArrayEquals(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 }, groupId.getDecodedId());
|
||||||
assertFalse(groupId.isMmsGroup());
|
assertFalse(groupId.isMms());
|
||||||
|
assertTrue(groupId.isV1());
|
||||||
|
assertFalse(groupId.isV2());
|
||||||
|
assertTrue(groupId.isPush());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = AssertionError.class)
|
@Test(expected = AssertionError.class)
|
||||||
public void bad_encoding__bad_prefix__parseNullable() {
|
public void bad_encoding__bad_prefix__parseNullable() {
|
||||||
GroupId.parseNullable("__BAD_PREFIX__!0001020305060708090b0c0d0e0f");
|
GroupId.parseNullable("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = AssertionError.class)
|
@Test(expected = AssertionError.class)
|
||||||
|
@ -80,7 +136,7 @@ public final class GroupIdTest {
|
||||||
|
|
||||||
@Test(expected = AssertionError.class)
|
@Test(expected = AssertionError.class)
|
||||||
public void bad_encoding__bad_prefix__parse() {
|
public void bad_encoding__bad_prefix__parse() {
|
||||||
GroupId.parse("__BAD_PREFIX__!0001020305060708090b0c0d0e0f");
|
GroupId.parse("__BAD_PREFIX__!000102030405060708090a0b0c0d0e0f");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test(expected = AssertionError.class)
|
@Test(expected = AssertionError.class)
|
||||||
|
@ -90,7 +146,7 @@ public final class GroupIdTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void get_bytes() {
|
public void get_bytes() {
|
||||||
byte[] bytes = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 };
|
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.v1(bytes);
|
||||||
|
|
||||||
assertArrayEquals(bytes, groupId.getDecodedId());
|
assertArrayEquals(bytes, groupId.getDecodedId());
|
||||||
|
@ -98,8 +154,8 @@ public final class GroupIdTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void equality() {
|
public void equality() {
|
||||||
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
|
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, 5, 6, 7, 8, 9, 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 });
|
||||||
|
|
||||||
assertNotSame(groupId1, groupId2);
|
assertNotSame(groupId1, groupId2);
|
||||||
assertEquals(groupId1, groupId2);
|
assertEquals(groupId1, groupId2);
|
||||||
|
@ -108,8 +164,8 @@ public final class GroupIdTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inequality_by_bytes() {
|
public void inequality_by_bytes() {
|
||||||
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
|
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, 5, 6, 7, 8, 9, 11, 12, 13, 14, 16 });
|
GroupId groupId2 = GroupId.v1(new byte[]{ 0, 3, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
|
||||||
|
|
||||||
assertNotSame(groupId1, groupId2);
|
assertNotSame(groupId1, groupId2);
|
||||||
assertNotEquals(groupId1, groupId2);
|
assertNotEquals(groupId1, groupId2);
|
||||||
|
@ -118,8 +174,8 @@ public final class GroupIdTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inequality_of_sms_and_mms() {
|
public void inequality_of_sms_and_mms() {
|
||||||
GroupId groupId1 = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
|
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.mms(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 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);
|
assertNotSame(groupId1, groupId2);
|
||||||
assertNotEquals(groupId1, groupId2);
|
assertNotEquals(groupId1, groupId2);
|
||||||
|
@ -128,8 +184,84 @@ public final class GroupIdTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void inequality_with_null() {
|
public void inequality_with_null() {
|
||||||
GroupId groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15 });
|
GroupId groupId = GroupId.v1(new byte[]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 });
|
||||||
|
|
||||||
assertNotEquals(groupId, null);
|
assertNotEquals(groupId, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void require_mms() {
|
||||||
|
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
|
GroupId.Mms mms = groupId.requireMms();
|
||||||
|
|
||||||
|
assertSame(groupId, mms);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void require_v1_and_push() {
|
||||||
|
GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
|
GroupId.V1 v1 = groupId.requireV1();
|
||||||
|
GroupId.Push push = groupId.requirePush();
|
||||||
|
|
||||||
|
assertSame(groupId, v1);
|
||||||
|
assertSame(groupId, push);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void require_v2_and_push() {
|
||||||
|
GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e");
|
||||||
|
|
||||||
|
GroupId.V2 v2 = groupId.requireV2 ();
|
||||||
|
GroupId.Push push = groupId.requirePush();
|
||||||
|
|
||||||
|
assertSame(groupId, v2);
|
||||||
|
assertSame(groupId, push);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
|
public void cannot_require_push_of_mms() {
|
||||||
|
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
|
groupId.requirePush();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
|
public void cannot_require_v1_of_mms() {
|
||||||
|
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
|
groupId.requireV1();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
|
public void cannot_require_v2_of_mms() {
|
||||||
|
GroupId groupId = GroupId.parse("__signal_mms_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
|
groupId.requireV2();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
|
public void cannot_require_v1_of_v2() {
|
||||||
|
GroupId groupId = GroupId.parse("__textsecure_group__!9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e");
|
||||||
|
|
||||||
|
groupId.requireV1();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
|
public void cannot_require_v2_of_v1() {
|
||||||
|
GroupId groupId = GroupId.parse("__textsecure_group__!000102030405060708090a0b0c0d0e0f");
|
||||||
|
|
||||||
|
groupId.requireV2();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
|
public void cannot_create_v1_with_a_v2_length() throws IOException {
|
||||||
|
GroupId.v1(Hex.fromStringCondensed("9f475f59b2518bff6df22e820803f0e3585bd99e686fa7e7fbfc2f92fd5d953e"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = AssertionError.class)
|
||||||
|
public void cannot_create_v2_with_a_v1_length() throws IOException {
|
||||||
|
GroupId.v2(Hex.fromStringCondensed("000102030405060708090a0b0c0d0e0f"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
package org.thoughtcrime.securesms.groups;
|
||||||
|
|
||||||
|
import org.signal.zkgroup.internal.Native;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.fail;
|
||||||
|
import static org.junit.Assume.assumeNoException;
|
||||||
|
|
||||||
|
class ZkGroupLibraryUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to initialize the ZkGroup Native class, which will load the native binaries.
|
||||||
|
* <p>
|
||||||
|
* If that fails to link, then on Unix, it will fail as we rely on that for CI.
|
||||||
|
* <p>
|
||||||
|
* If that fails to link, and it's not Unix, it will skip the test via assumption violation.
|
||||||
|
*/
|
||||||
|
static void assumeZkGroupSupportedOnOS() {
|
||||||
|
try {
|
||||||
|
Class.forName(Native.class.getName());
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
fail();
|
||||||
|
} catch (UnsatisfiedLinkError e) {
|
||||||
|
String osName = System.getProperty("os.name");
|
||||||
|
|
||||||
|
if (isUnix(osName)) {
|
||||||
|
fail("Not able to link native ZkGroup on a key OS: " + osName);
|
||||||
|
} else {
|
||||||
|
assumeNoException("Not able to link native ZkGroup on this operating system: " + osName, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isUnix(String osName) {
|
||||||
|
assertNotNull(osName);
|
||||||
|
osName = osName.toLowerCase();
|
||||||
|
return osName.contains("nix") || osName.contains("nux") || osName.contains("aix");
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue