Remove UUIDs from GV1 membership lists.

This commit is contained in:
Greyson Parrelli 2020-07-27 16:37:08 -04:00
parent 1ab61beeb9
commit 5c110ca359
10 changed files with 60 additions and 59 deletions

View File

@ -36,6 +36,7 @@ import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupC
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
import java.io.IOException; import java.io.IOException;
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;
@ -122,12 +123,15 @@ final class GroupManagerV1 {
RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId); RecipientId groupRecipientId = DatabaseFactory.getRecipientDatabase(context).getOrInsertFromGroupId(groupId);
Recipient groupRecipient = Recipient.resolved(groupRecipientId); Recipient groupRecipient = Recipient.resolved(groupRecipientId);
List<GroupContext.Member> uuidMembers = new LinkedList<>(); List<GroupContext.Member> uuidMembers = new ArrayList<>(members.size());
List<String> e164Members = new LinkedList<>(); List<String> e164Members = new ArrayList<>(members.size());
for (RecipientId member : members) { for (RecipientId member : members) {
Recipient recipient = Recipient.resolved(member); Recipient recipient = Recipient.resolved(member);
uuidMembers.add(GroupV1MessageProcessor.createMember(RecipientUtil.toSignalServiceAddress(context, recipient))); if (recipient.hasE164()) {
e164Members.add(recipient.requireE164());
uuidMembers.add(GroupV1MessageProcessor.createMember(recipient.requireE164()));
}
} }
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder() GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
@ -135,7 +139,9 @@ final class GroupManagerV1 {
.setType(GroupContext.Type.UPDATE) .setType(GroupContext.Type.UPDATE)
.addAllMembersE164(e164Members) .addAllMembersE164(e164Members)
.addAllMembers(uuidMembers); .addAllMembers(uuidMembers);
if (groupName != null) groupContextBuilder.setName(groupName); if (groupName != null) groupContextBuilder.setName(groupName);
GroupContext groupContext = groupContextBuilder.build(); GroupContext groupContext = groupContextBuilder.build();
if (avatar != null) { if (avatar != null) {

View File

@ -101,7 +101,7 @@ public final class GroupV1MessageProcessor {
if (group.getMembers().isPresent()) { if (group.getMembers().isPresent()) {
for (SignalServiceAddress member : group.getMembers().get()) { for (SignalServiceAddress member : group.getMembers().get()) {
members.add(Recipient.externalPush(context, member).getId()); members.add(Recipient.externalGV1Member(context, member).getId());
} }
} }
@ -131,8 +131,10 @@ public final class GroupV1MessageProcessor {
Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers()); Set<RecipientId> recordMembers = new HashSet<>(groupRecord.getMembers());
Set<RecipientId> messageMembers = new HashSet<>(); Set<RecipientId> messageMembers = new HashSet<>();
for (SignalServiceAddress messageMember : group.getMembers().get()) { if (group.getMembers().isPresent()) {
messageMembers.add(Recipient.externalPush(context, messageMember).getId()); for (SignalServiceAddress messageMember : group.getMembers().get()) {
messageMembers.add(Recipient.externalGV1Member(context, messageMember).getId());
}
} }
Set<RecipientId> addedMembers = new HashSet<>(messageMembers); Set<RecipientId> addedMembers = new HashSet<>(messageMembers);
@ -150,18 +152,19 @@ public final class GroupV1MessageProcessor {
database.updateMembers(id, new LinkedList<>(unionMembers)); database.updateMembers(id, new LinkedList<>(unionMembers));
builder.clearMembers(); builder.clearMembers();
builder.clearMembersE164();
for (RecipientId addedMember : addedMembers) { for (RecipientId addedMember : addedMembers) {
Recipient recipient = Recipient.resolved(addedMember); Recipient recipient = Recipient.resolved(addedMember);
if (recipient.getE164().isPresent()) { if (recipient.getE164().isPresent()) {
builder.addMembersE164(recipient.getE164().get()); builder.addMembersE164(recipient.requireE164());
builder.addMembers(createMember(recipient.requireE164()));
} }
builder.addMembers(createMember(RecipientUtil.toSignalServiceAddress(context, recipient)));
} }
} else { } else {
builder.clearMembers(); builder.clearMembers();
builder.clearMembersE164();
} }
if (missingMembers.size() > 0) { if (missingMembers.size() > 0) {
@ -287,6 +290,8 @@ public final class GroupV1MessageProcessor {
.map(a -> a.getNumber().get()) .map(a -> a.getNumber().get())
.toList()); .toList());
builder.addAllMembers(Stream.of(group.getMembers().get()) builder.addAllMembers(Stream.of(group.getMembers().get())
.filter(address -> address.getNumber().isPresent())
.map(address -> address.getNumber().get())
.map(GroupV1MessageProcessor::createMember) .map(GroupV1MessageProcessor::createMember)
.toList()); .toList());
} }
@ -294,17 +299,9 @@ public final class GroupV1MessageProcessor {
return builder; return builder;
} }
public static GroupContext.Member createMember(SignalServiceAddress address) { public static GroupContext.Member createMember(@NonNull String e164) {
GroupContext.Member.Builder member = GroupContext.Member.newBuilder(); GroupContext.Member.Builder member = GroupContext.Member.newBuilder();
member.setE164(e164);
if (address.getUuid().isPresent()) {
member.setUuid(address.getUuid().get().toString());
}
if (address.getNumber().isPresent()) {
member.setE164(address.getNumber().get());
}
return member.build(); return member.build();
} }
} }

View File

@ -323,8 +323,8 @@ public final class PushGroupSendJob extends PushSendJob {
GroupContext groupContext = properties.getGroupContext(); GroupContext groupContext = properties.getGroupContext();
SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0); SignalServiceAttachment avatar = attachmentPointers.isEmpty() ? null : attachmentPointers.get(0);
SignalServiceGroup.Type type = properties.isQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE; SignalServiceGroup.Type type = properties.isQuit() ? SignalServiceGroup.Type.QUIT : SignalServiceGroup.Type.UPDATE;
List<SignalServiceAddress> members = Stream.of(groupContext.getMembersList()) List<SignalServiceAddress> members = Stream.of(groupContext.getMembersE164List())
.map(m -> new SignalServiceAddress(UuidUtil.parseOrNull(m.getUuid()), m.getE164())) .map(e164 -> new SignalServiceAddress(null, e164))
.toList(); .toList();
SignalServiceGroup group = new SignalServiceGroup(type, groupId.getDecodedId(), groupContext.getName(), members, avatar); SignalServiceGroup group = new SignalServiceGroup(type, groupId.getDecodedId(), groupContext.getName(), members, avatar);
SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder() SignalServiceDataMessage groupDataMessage = SignalServiceDataMessage.newBuilder()

View File

@ -3,6 +3,8 @@ package org.thoughtcrime.securesms.mms;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.annimon.stream.Stream;
import org.signal.storageservice.protos.groups.local.DecryptedMember; import org.signal.storageservice.protos.groups.local.DecryptedMember;
import org.signal.zkgroup.InvalidInputException; import org.signal.zkgroup.InvalidInputException;
import org.signal.zkgroup.groups.GroupMasterKey; import org.signal.zkgroup.groups.GroupMasterKey;
@ -11,6 +13,7 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.recipients.RecipientId;
import org.thoughtcrime.securesms.util.Base64; import org.thoughtcrime.securesms.util.Base64;
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil; import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.util.UuidUtil; import org.whispersystems.signalservice.api.util.UuidUtil;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContext;
import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2; import org.whispersystems.signalservice.internal.push.SignalServiceProtos.GroupContextV2;
@ -123,20 +126,13 @@ public final class MessageGroupContext {
@Override @Override
public @NonNull List<RecipientId> getMembersListExcludingSelf() { public @NonNull List<RecipientId> getMembersListExcludingSelf() {
List<GroupContext.Member> membersList = groupContext.getMembersList(); RecipientId selfId = Recipient.self().getId();
if (membersList.isEmpty()) {
return Collections.emptyList();
} else {
LinkedList<RecipientId> members = new LinkedList<>();
for (GroupContext.Member member : membersList) { return Stream.of(groupContext.getMembersE164List())
RecipientId recipient = RecipientId.from(UuidUtil.parseOrNull(member.getUuid()), member.getE164()); .map(e164 -> new SignalServiceAddress(null, e164))
if (!Recipient.self().getId().equals(recipient)) { .map(RecipientId::from)
members.add(recipient); .filterNot(selfId::equals)
} .toList();
}
return members;
}
} }
} }

View File

@ -157,6 +157,20 @@ public class Recipient {
return externalPush(context, signalServiceAddress.getUuid().orNull(), signalServiceAddress.getNumber().orNull(), false); return externalPush(context, signalServiceAddress.getUuid().orNull(), signalServiceAddress.getNumber().orNull(), false);
} }
/**
* Returns a fully-populated {@link Recipient} based off of a {@link SignalServiceAddress},
* creating one in the database if necessary. We special-case GV1 members because we want to
* prioritize E164 addresses and not use the UUIDs if possible.
*/
@WorkerThread
public static @NonNull Recipient externalGV1Member(@NonNull Context context, @NonNull SignalServiceAddress address) {
if (address.getNumber().isPresent()) {
return externalPush(context, null, address.getNumber().get(), false);
} else {
return externalPush(context, address.getUuid().orNull(), null, false);
}
}
/** /**
* Returns a fully-populated {@link Recipient} based off of a {@link SignalServiceAddress}, * Returns a fully-populated {@link Recipient} based off of a {@link SignalServiceAddress},
* creating one in the database if necessary. This should only used for high-trust sources, * creating one in the database if necessary. This should only used for high-trust sources,

View File

@ -1060,19 +1060,12 @@ public class SignalServiceMessageSender {
for (SignalServiceAddress address : group.getMembers().get()) { for (SignalServiceAddress address : group.getMembers().get()) {
if (address.getNumber().isPresent()) { if (address.getNumber().isPresent()) {
builder.addMembersE164(address.getNumber().get()); builder.addMembersE164(address.getNumber().get());
}
GroupContext.Member.Builder memberBuilder = GroupContext.Member.newBuilder(); GroupContext.Member.Builder memberBuilder = GroupContext.Member.newBuilder();
if (address.getUuid().isPresent()) {
memberBuilder.setUuid(address.getUuid().get().toString());
}
if (address.getNumber().isPresent()) {
memberBuilder.setE164(address.getNumber().get()); memberBuilder.setE164(address.getNumber().get());
}
builder.addMembers(memberBuilder.build()); builder.addMembers(memberBuilder.build());
}
} }
} }

View File

@ -873,8 +873,8 @@ public final class SignalServiceContent {
members = new ArrayList<>(content.getGroup().getMembersCount()); members = new ArrayList<>(content.getGroup().getMembersCount());
for (SignalServiceProtos.GroupContext.Member member : content.getGroup().getMembersList()) { for (SignalServiceProtos.GroupContext.Member member : content.getGroup().getMembersList()) {
if (SignalServiceAddress.isValidAddress(member.getUuid(), member.getE164())) { if (SignalServiceAddress.isValidAddress(null, member.getE164())) {
members.add(new SignalServiceAddress(UuidUtil.parseOrNull(member.getUuid()), member.getE164())); members.add(new SignalServiceAddress(null, member.getE164()));
} else { } else {
throw new ProtocolInvalidMessageException(new InvalidMessageException("GroupContext.Member had no address!"), null, 0); throw new ProtocolInvalidMessageException(new InvalidMessageException("GroupContext.Member had no address!"), null, 0);
} }

View File

@ -61,8 +61,8 @@ public class DeviceGroupsInputStream extends ChunkedInputStream{
List<SignalServiceAddress> addressMembers = new ArrayList<>(members.size()); List<SignalServiceAddress> addressMembers = new ArrayList<>(members.size());
for (GroupDetails.Member member : members) { for (GroupDetails.Member member : members) {
if (SignalServiceAddress.isValidAddress(member.getUuid(), member.getE164())) { if (SignalServiceAddress.isValidAddress(null, member.getE164())) {
addressMembers.add(new SignalServiceAddress(UuidUtil.parseOrNull(member.getUuid()), member.getE164())); addressMembers.add(new SignalServiceAddress(null, member.getE164()));
} else { } else {
throw new IOException("Missing group member address!"); throw new IOException("Missing group member address!");
} }

View File

@ -65,18 +65,13 @@ public class DeviceGroupsOutputStream extends ChunkedOutputStream {
List<String> membersE164 = new ArrayList<>(group.getMembers().size()); List<String> membersE164 = new ArrayList<>(group.getMembers().size());
for (SignalServiceAddress address : group.getMembers()) { for (SignalServiceAddress address : group.getMembers()) {
GroupDetails.Member.Builder builder = GroupDetails.Member.newBuilder();
if (address.getUuid().isPresent()) {
builder.setUuid(address.getUuid().get().toString());
}
if (address.getNumber().isPresent()) { if (address.getNumber().isPresent()) {
builder.setE164(address.getNumber().get());
membersE164.add(address.getNumber().get()); membersE164.add(address.getNumber().get());
}
members.add(builder.build()); GroupDetails.Member.Builder builder = GroupDetails.Member.newBuilder();
builder.setE164(address.getNumber().get());
members.add(builder.build());
}
} }
groupDetails.addAllMembers(members); groupDetails.addAllMembers(members);

View File

@ -435,7 +435,7 @@ message GroupContext {
} }
message Member { message Member {
optional string uuid = 1; // 1 is reserved
optional string e164 = 2; optional string e164 = 2;
} }
@ -479,7 +479,7 @@ message GroupDetails {
} }
message Member { message Member {
optional string uuid = 1; // 1 is reserved
optional string e164 = 2; optional string e164 = 2;
} }