mirror of
https://github.com/oxen-io/session-android.git
synced 2025-06-08 18:28:33 +00:00
Support Groups v2 Change Epochs.
This commit is contained in:
parent
70977e5228
commit
b10fc6a0b0
@ -466,7 +466,8 @@ final class GroupManagerV2 {
|
|||||||
GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey(groupMasterKey));
|
GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(GroupSecretParams.deriveFromMasterKey(groupMasterKey));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return groupOperations.decryptChange(GroupChange.parseFrom(signedGroupChange), true);
|
return groupOperations.decryptChange(GroupChange.parseFrom(signedGroupChange), true)
|
||||||
|
.orNull();
|
||||||
} catch (VerificationFailedException | InvalidGroupStateException | InvalidProtocolBufferException e) {
|
} catch (VerificationFailedException | InvalidGroupStateException | InvalidProtocolBufferException e) {
|
||||||
Log.w(TAG, "Unable to verify supplied group change", e);
|
Log.w(TAG, "Unable to verify supplied group change", e);
|
||||||
}
|
}
|
||||||
|
@ -341,7 +341,12 @@ public final class GroupsV2StateProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (DecryptedGroupHistoryEntry entry : groupStatesFromRevision) {
|
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;
|
return history;
|
||||||
|
@ -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.DecryptedGroup;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
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.
|
* Pair of a {@link DecryptedGroup} and the {@link DecryptedGroupChange} for that version.
|
||||||
*/
|
*/
|
||||||
public final class DecryptedGroupHistoryEntry {
|
public final class DecryptedGroupHistoryEntry {
|
||||||
|
|
||||||
private final DecryptedGroup group;
|
private final Optional<DecryptedGroup> group;
|
||||||
private final DecryptedGroupChange change;
|
private final Optional<DecryptedGroupChange> change;
|
||||||
|
|
||||||
DecryptedGroupHistoryEntry(DecryptedGroup group, DecryptedGroupChange change) {
|
DecryptedGroupHistoryEntry(Optional<DecryptedGroup> group, Optional<DecryptedGroupChange> change)
|
||||||
if (group.getRevision() != change.getRevision()) {
|
throws InvalidGroupStateException
|
||||||
throw new AssertionError();
|
{
|
||||||
|
if (group.isPresent() && change.isPresent() && group.get().getRevision() != change.get().getRevision()) {
|
||||||
|
throw new InvalidGroupStateException();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.group = group;
|
this.group = group;
|
||||||
this.change = change;
|
this.change = change;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DecryptedGroup getGroup() {
|
public Optional<DecryptedGroup> getGroup() {
|
||||||
return group;
|
return group;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DecryptedGroupChange getChange() {
|
public Optional<DecryptedGroupChange> getChange() {
|
||||||
return change;
|
return change;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,7 @@ import org.signal.zkgroup.auth.AuthCredentialResponse;
|
|||||||
import org.signal.zkgroup.auth.ClientZkAuthOperations;
|
import org.signal.zkgroup.auth.ClientZkAuthOperations;
|
||||||
import org.signal.zkgroup.groups.ClientZkGroupCipher;
|
import org.signal.zkgroup.groups.ClientZkGroupCipher;
|
||||||
import org.signal.zkgroup.groups.GroupSecretParams;
|
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||||
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
|
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -100,12 +101,8 @@ public final class GroupsV2Api {
|
|||||||
GroupsV2Operations.GroupOperations groupOperations = groupsOperations.forGroup(groupSecretParams);
|
GroupsV2Operations.GroupOperations groupOperations = groupsOperations.forGroup(groupSecretParams);
|
||||||
|
|
||||||
for (GroupChanges.GroupChangeState change : changesList) {
|
for (GroupChanges.GroupChangeState change : changesList) {
|
||||||
DecryptedGroup decryptedGroup = groupOperations.decryptGroup(change.getGroupState());
|
Optional<DecryptedGroup> decryptedGroup = change.hasGroupState () ? Optional.of(groupOperations.decryptGroup(change.getGroupState())) : Optional.absent();
|
||||||
DecryptedGroupChange decryptedChange = groupOperations.decryptChange(change.getGroupChange(), false);
|
Optional<DecryptedGroupChange> decryptedChange = change.hasGroupChange() ? groupOperations.decryptChange(change.getGroupChange(), false) : Optional.absent();
|
||||||
|
|
||||||
if (decryptedChange.getRevision() != decryptedGroup.getRevision()) {
|
|
||||||
throw new InvalidGroupStateException();
|
|
||||||
}
|
|
||||||
|
|
||||||
result.add(new DecryptedGroupHistoryEntry(decryptedGroup, decryptedChange));
|
result.add(new DecryptedGroupHistoryEntry(decryptedGroup, decryptedChange));
|
||||||
}
|
}
|
||||||
|
@ -39,6 +39,7 @@ import java.security.SecureRandom;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -52,6 +53,9 @@ public final class GroupsV2Operations {
|
|||||||
/** Used for undecryptable pending invites */
|
/** Used for undecryptable pending invites */
|
||||||
public static final UUID UNKNOWN_UUID = UuidUtil.UNKNOWN_UUID;
|
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 ServerPublicParams serverPublicParams;
|
||||||
private final ClientZkProfileOperations clientZkProfileOperations;
|
private final ClientZkProfileOperations clientZkProfileOperations;
|
||||||
private final ClientZkAuthOperations clientZkAuthOperations;
|
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
|
* @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.
|
* are not going to pass to other clients.
|
||||||
* <p>
|
* <p>
|
||||||
* Also, if you know it's version 0, do not verify because changes for version 0
|
* Also, if you know it's version 0, do not verify because changes for version 0
|
||||||
* are not signed, but should be empty.
|
* 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
|
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)
|
public DecryptedGroupChange decryptChange(GroupChange.Actions actions)
|
||||||
|
@ -132,6 +132,7 @@ message GroupChange {
|
|||||||
|
|
||||||
bytes actions = 1;
|
bytes actions = 1;
|
||||||
bytes serverSignature = 2;
|
bytes serverSignature = 2;
|
||||||
|
uint32 changeEpoch = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
message GroupChanges {
|
message GroupChanges {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user