Skip to content

Commit

Permalink
Support Groups v2 Change Epochs.
Browse files Browse the repository at this point in the history
  • Loading branch information
alan-signal authored and greyson-signal committed Jul 19, 2020
1 parent 70977e5 commit b10fc6a
Show file tree
Hide file tree
Showing 6 changed files with 42 additions and 25 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,8 @@ private DecryptedGroupChange getDecryptedGroupChange(@Nullable byte[] signedGrou
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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -341,7 +341,12 @@ private List<ServerGroupLogEntry> getFullMemberHistory(@NonNull UUID selfUuid, i
}

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,32 @@

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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
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;
Expand Down Expand Up @@ -100,12 +101,8 @@ public List<DecryptedGroupHistoryEntry> getGroupHistory(GroupSecretParams groupS
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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;

Expand All @@ -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;
Expand Down Expand Up @@ -289,18 +293,24 @@ public DecryptedGroup decryptGroup(Group group)
}

/**
* @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();
}

GroupChange.Actions actions = verifySignature ? getVerifiedActions(groupChange) : getActions(groupChange);

return decryptChange(actions);
return Optional.of(decryptChange(actions));
}

public DecryptedGroupChange decryptChange(GroupChange.Actions actions)
Expand Down
5 changes: 3 additions & 2 deletions libsignal/service/src/main/proto/Groups.proto
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down

0 comments on commit b10fc6a

Please sign in to comment.