Support Groups v2 Change Epochs.

This commit is contained in:
Alan Evans 2020-07-16 16:51:43 -03:00 committed by Greyson Parrelli
parent 70977e5228
commit b10fc6a0b0
6 changed files with 42 additions and 25 deletions

View File

@ -466,7 +466,8 @@ final class GroupManagerV2 {
GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey(groupMasterKey));
try {
return groupOperations.decryptChange(GroupChange.parseFrom(signedGroupChange), true);
return groupOperations.decryptChange(GroupChange.parseFrom(signedGroupChange), true)
.orNull();
} catch (VerificationFailedException | InvalidGroupStateException | InvalidProtocolBufferException e) {
Log.w(TAG, "Unable to verify supplied group change", e);
}

View File

@ -341,7 +341,12 @@ public final class GroupsV2StateProcessor {
}
for (DecryptedGroupHistoryEntry entry : groupStatesFromRevision) {
history.add(new ServerGroupLogEntry(entry.getGroup(), ignoreServerChanges ? null : entry.getChange()));
DecryptedGroup group = entry.getGroup().orNull();
DecryptedGroupChange change = ignoreServerChanges ? null : entry.getChange().orNull();
if (group != null || change != null) {
history.add(new ServerGroupLogEntry(group, change));
}
}
return history;

View File

@ -2,29 +2,32 @@ package org.whispersystems.signalservice.api.groupsv2;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
import org.whispersystems.libsignal.util.guava.Optional;
/**
* Pair of a {@link DecryptedGroup} and the {@link DecryptedGroupChange} for that version.
*/
public final class DecryptedGroupHistoryEntry {
private final DecryptedGroup group;
private final DecryptedGroupChange change;
private final Optional<DecryptedGroup> group;
private final Optional<DecryptedGroupChange> change;
DecryptedGroupHistoryEntry(DecryptedGroup group, DecryptedGroupChange change) {
if (group.getRevision() != change.getRevision()) {
throw new AssertionError();
DecryptedGroupHistoryEntry(Optional<DecryptedGroup> group, Optional<DecryptedGroupChange> change)
throws InvalidGroupStateException
{
if (group.isPresent() && change.isPresent() && group.get().getRevision() != change.get().getRevision()) {
throw new InvalidGroupStateException();
}
this.group = group;
this.change = change;
}
public DecryptedGroup getGroup() {
public Optional<DecryptedGroup> getGroup() {
return group;
}
public DecryptedGroupChange getChange() {
public Optional<DecryptedGroupChange> getChange() {
return change;
}
}

View File

@ -17,6 +17,7 @@ import org.signal.zkgroup.auth.AuthCredentialResponse;
import org.signal.zkgroup.auth.ClientZkAuthOperations;
import org.signal.zkgroup.groups.ClientZkGroupCipher;
import org.signal.zkgroup.groups.GroupSecretParams;
import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import java.io.IOException;
@ -100,12 +101,8 @@ public final class GroupsV2Api {
GroupsV2Operations.GroupOperations groupOperations = groupsOperations.forGroup(groupSecretParams);
for (GroupChanges.GroupChangeState change : changesList) {
DecryptedGroup decryptedGroup = groupOperations.decryptGroup(change.getGroupState());
DecryptedGroupChange decryptedChange = groupOperations.decryptChange(change.getGroupChange(), false);
if (decryptedChange.getRevision() != decryptedGroup.getRevision()) {
throw new InvalidGroupStateException();
}
Optional<DecryptedGroup> decryptedGroup = change.hasGroupState () ? Optional.of(groupOperations.decryptGroup(change.getGroupState())) : Optional.absent();
Optional<DecryptedGroupChange> decryptedChange = change.hasGroupChange() ? groupOperations.decryptChange(change.getGroupChange(), false) : Optional.absent();
result.add(new DecryptedGroupHistoryEntry(decryptedGroup, decryptedChange));
}

View File

@ -39,6 +39,7 @@ import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
@ -52,6 +53,9 @@ public final class GroupsV2Operations {
/** Used for undecryptable pending invites */
public static final UUID UNKNOWN_UUID = UuidUtil.UNKNOWN_UUID;
/** Highest change epoch this class knows now to decrypt */
public static final int HIGHEST_KNOWN_EPOCH = 0;
private final ServerPublicParams serverPublicParams;
private final ClientZkProfileOperations clientZkProfileOperations;
private final ClientZkAuthOperations clientZkAuthOperations;
@ -289,18 +293,24 @@ public final class GroupsV2Operations {
}
/**
* @param verify You might want to avoid verification if you already know it's correct, or you
* are not going to pass to other clients.
* <p>
* Also, if you know it's version 0, do not verify because changes for version 0
* are not signed, but should be empty.
* @param verifySignature You might want to avoid verification if you already know it's correct, or you
* are not going to pass to other clients.
* <p>
* Also, if you know it's version 0, do not verify because changes for version 0
* are not signed, but should be empty.
* @return {@link Optional#absent} if the epoch for the change is higher that this code can decrypt.
*/
public DecryptedGroupChange decryptChange(GroupChange groupChange, boolean verify)
public Optional<DecryptedGroupChange> decryptChange(GroupChange groupChange, boolean verifySignature)
throws InvalidProtocolBufferException, VerificationFailedException, InvalidGroupStateException
{
GroupChange.Actions actions = verify ? getVerifiedActions(groupChange) : getActions(groupChange);
if (groupChange.getChangeEpoch() > HIGHEST_KNOWN_EPOCH) {
Log.w(TAG, String.format(Locale.US, "Ignoring change from Epoch %d. Highest known Epoch is %d", groupChange.getChangeEpoch(), HIGHEST_KNOWN_EPOCH));
return Optional.absent();
}
return decryptChange(actions);
GroupChange.Actions actions = verifySignature ? getVerifiedActions(groupChange) : getActions(groupChange);
return Optional.of(decryptChange(actions));
}
public DecryptedGroupChange decryptChange(GroupChange.Actions actions)

View File

@ -130,8 +130,9 @@ message GroupChange {
ModifyMembersAccessControlAction modifyMemberAccess = 14;
}
bytes actions = 1;
bytes serverSignature = 2;
bytes actions = 1;
bytes serverSignature = 2;
uint32 changeEpoch = 3;
}
message GroupChanges {