mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 10:30:38 +00:00
Implement updating of v2 groups
This commit is contained in:
parent
98dee97cc6
commit
1fd62ee342
3 changed files with 193 additions and 50 deletions
|
@ -43,6 +43,7 @@ import org.signal.libsignal.metadata.ProtocolLegacyMessageException;
|
||||||
import org.signal.libsignal.metadata.ProtocolNoSessionException;
|
import org.signal.libsignal.metadata.ProtocolNoSessionException;
|
||||||
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
|
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
|
||||||
import org.signal.libsignal.metadata.SelfSendException;
|
import org.signal.libsignal.metadata.SelfSendException;
|
||||||
|
import org.signal.storageservice.protos.groups.GroupChange;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
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;
|
||||||
|
@ -213,7 +214,9 @@ public class Manager implements Closeable {
|
||||||
this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential,
|
this.groupHelper = new GroupHelper(this::getRecipientProfileKeyCredential,
|
||||||
this::getRecipientProfile,
|
this::getRecipientProfile,
|
||||||
account::getSelfAddress,
|
account::getSelfAddress,
|
||||||
groupsV2Operations);
|
groupsV2Operations,
|
||||||
|
groupsV2Api,
|
||||||
|
this::getGroupAuthForToday);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
|
@ -752,36 +755,6 @@ public class Manager implements Closeable {
|
||||||
return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
|
return sendMessage(messageBuilder, g.getMembersWithout(account.getSelfAddress()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private GroupInfoV2 createGroupV2(
|
|
||||||
String name, Collection<SignalServiceAddress> members, InputStream avatar
|
|
||||||
) throws IOException {
|
|
||||||
byte[] avatarBytes = avatar == null ? null : IOUtils.readFully(avatar);
|
|
||||||
final GroupsV2Operations.NewGroup newGroup = groupHelper.createGroupV2(name, members, avatarBytes);
|
|
||||||
final GroupSecretParams groupSecretParams = newGroup.getGroupSecretParams();
|
|
||||||
|
|
||||||
final GroupsV2AuthorizationString groupAuthForToday;
|
|
||||||
final DecryptedGroup decryptedGroup;
|
|
||||||
try {
|
|
||||||
groupAuthForToday = getGroupAuthForToday(groupSecretParams);
|
|
||||||
groupsV2Api.putNewGroup(newGroup, groupAuthForToday);
|
|
||||||
decryptedGroup = groupsV2Api.getGroup(groupSecretParams, groupAuthForToday);
|
|
||||||
} catch (IOException | VerificationFailedException | InvalidGroupStateException e) {
|
|
||||||
System.err.println("Failed to create V2 group: " + e.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (decryptedGroup == null) {
|
|
||||||
System.err.println("Failed to create V2 group!");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final byte[] groupId = groupSecretParams.getPublicParams().getGroupIdentifier().serialize();
|
|
||||||
final GroupMasterKey masterKey = groupSecretParams.getMasterKey();
|
|
||||||
GroupInfoV2 g = new GroupInfoV2(groupId, masterKey);
|
|
||||||
g.setGroup(decryptedGroup);
|
|
||||||
|
|
||||||
return g;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Pair<byte[], List<SendMessageResult>> sendUpdateGroupMessage(
|
private Pair<byte[], List<SendMessageResult>> sendUpdateGroupMessage(
|
||||||
byte[] groupId, String name, Collection<SignalServiceAddress> members, String avatarFile
|
byte[] groupId, String name, Collection<SignalServiceAddress> members, String avatarFile
|
||||||
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
|
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
|
||||||
|
@ -789,8 +762,7 @@ public class Manager implements Closeable {
|
||||||
SignalServiceDataMessage.Builder messageBuilder;
|
SignalServiceDataMessage.Builder messageBuilder;
|
||||||
if (groupId == null) {
|
if (groupId == null) {
|
||||||
// Create new group
|
// Create new group
|
||||||
InputStream avatar = avatarFile == null ? null : new FileInputStream(avatarFile);
|
GroupInfoV2 gv2 = groupHelper.createGroupV2(name, members, avatarFile);
|
||||||
GroupInfoV2 gv2 = createGroupV2(name, members, avatar);
|
|
||||||
if (gv2 == null) {
|
if (gv2 == null) {
|
||||||
GroupInfoV1 gv1 = new GroupInfoV1(KeyUtils.createGroupId());
|
GroupInfoV1 gv1 = new GroupInfoV1(KeyUtils.createGroupId());
|
||||||
gv1.addMembers(Collections.singleton(account.getSelfAddress()));
|
gv1.addMembers(Collections.singleton(account.getSelfAddress()));
|
||||||
|
@ -798,18 +770,41 @@ public class Manager implements Closeable {
|
||||||
messageBuilder = getGroupUpdateMessageBuilder(gv1);
|
messageBuilder = getGroupUpdateMessageBuilder(gv1);
|
||||||
g = gv1;
|
g = gv1;
|
||||||
} else {
|
} else {
|
||||||
messageBuilder = getGroupUpdateMessageBuilder(gv2);
|
messageBuilder = getGroupUpdateMessageBuilder(gv2, null);
|
||||||
g = gv2;
|
g = gv2;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
GroupInfo group = getGroupForSending(groupId);
|
GroupInfo group = getGroupForSending(groupId);
|
||||||
if (!(group instanceof GroupInfoV1)) {
|
if (group instanceof GroupInfoV2) {
|
||||||
throw new RuntimeException("TODO Not implemented!");
|
Pair<DecryptedGroup, GroupChange> groupGroupChangePair = null;
|
||||||
|
if (members != null) {
|
||||||
|
final Set<SignalServiceAddress> newMembers = new HashSet<>(members);
|
||||||
|
newMembers.removeAll(group.getMembers());
|
||||||
|
if (newMembers.size() > 0) {
|
||||||
|
groupGroupChangePair = groupHelper.updateGroupV2((GroupInfoV2) group, newMembers);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (groupGroupChangePair == null || name != null || avatarFile != null) {
|
||||||
|
if (groupGroupChangePair != null) {
|
||||||
|
((GroupInfoV2) group).setGroup(groupGroupChangePair.first());
|
||||||
|
messageBuilder = getGroupUpdateMessageBuilder((GroupInfoV2) group,
|
||||||
|
groupGroupChangePair.second().toByteArray());
|
||||||
|
sendMessage(messageBuilder, group.getMembersWithout(account.getSelfAddress()));
|
||||||
|
}
|
||||||
|
|
||||||
|
groupGroupChangePair = groupHelper.updateGroupV2((GroupInfoV2) group, name, avatarFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
((GroupInfoV2) group).setGroup(groupGroupChangePair.first());
|
||||||
|
messageBuilder = getGroupUpdateMessageBuilder((GroupInfoV2) group,
|
||||||
|
groupGroupChangePair.second().toByteArray());
|
||||||
|
g = group;
|
||||||
|
} else {
|
||||||
|
GroupInfoV1 gv1 = (GroupInfoV1) group;
|
||||||
|
updateGroupV1(gv1, name, members, avatarFile);
|
||||||
|
messageBuilder = getGroupUpdateMessageBuilder(gv1);
|
||||||
|
g = gv1;
|
||||||
}
|
}
|
||||||
GroupInfoV1 gv1 = (GroupInfoV1) group;
|
|
||||||
updateGroupV1(gv1, name, members, avatarFile);
|
|
||||||
messageBuilder = getGroupUpdateMessageBuilder(gv1);
|
|
||||||
g = gv1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
account.getGroupStore().updateGroup(g);
|
account.getGroupStore().updateGroup(g);
|
||||||
|
@ -899,11 +894,10 @@ public class Manager implements Closeable {
|
||||||
.withExpiration(g.getMessageExpirationTime());
|
.withExpiration(g.getMessageExpirationTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV2 g) {
|
private SignalServiceDataMessage.Builder getGroupUpdateMessageBuilder(GroupInfoV2 g, byte[] signedGroupChange) {
|
||||||
SignalServiceGroupV2.Builder group = SignalServiceGroupV2.newBuilder(g.getMasterKey())
|
SignalServiceGroupV2.Builder group = SignalServiceGroupV2.newBuilder(g.getMasterKey())
|
||||||
.withRevision(g.getGroup().getRevision())
|
.withRevision(g.getGroup().getRevision())
|
||||||
// .withSignedGroupChange() // TODO
|
.withSignedGroupChange(signedGroupChange);
|
||||||
;
|
|
||||||
return SignalServiceDataMessage.newBuilder()
|
return SignalServiceDataMessage.newBuilder()
|
||||||
.asGroupMessage(group.build())
|
.asGroupMessage(group.build())
|
||||||
.withExpiration(g.getMessageExpirationTime());
|
.withExpiration(g.getMessageExpirationTime());
|
||||||
|
@ -1427,16 +1421,20 @@ public class Manager implements Closeable {
|
||||||
|
|
||||||
private GroupsV2AuthorizationString getGroupAuthForToday(
|
private GroupsV2AuthorizationString getGroupAuthForToday(
|
||||||
final GroupSecretParams groupSecretParams
|
final GroupSecretParams groupSecretParams
|
||||||
) throws IOException, VerificationFailedException {
|
) throws IOException {
|
||||||
final int today = currentTimeDays();
|
final int today = currentTimeDays();
|
||||||
// Returns credentials for the next 7 days
|
// Returns credentials for the next 7 days
|
||||||
final HashMap<Integer, AuthCredentialResponse> credentials = groupsV2Api.getCredentials(today);
|
final HashMap<Integer, AuthCredentialResponse> credentials = groupsV2Api.getCredentials(today);
|
||||||
// TODO cache credentials until they expire
|
// TODO cache credentials until they expire
|
||||||
AuthCredentialResponse authCredentialResponse = credentials.get(today);
|
AuthCredentialResponse authCredentialResponse = credentials.get(today);
|
||||||
return groupsV2Api.getGroupsV2AuthorizationString(account.getUuid(),
|
try {
|
||||||
today,
|
return groupsV2Api.getGroupsV2AuthorizationString(account.getUuid(),
|
||||||
groupSecretParams,
|
today,
|
||||||
authCredentialResponse);
|
groupSecretParams,
|
||||||
|
authCredentialResponse);
|
||||||
|
} catch (VerificationFailedException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<HandleAction> handleSignalServiceDataMessage(
|
private List<HandleAction> handleSignalServiceDataMessage(
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
|
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
public interface GroupAuthorizationProvider {
|
||||||
|
|
||||||
|
GroupsV2AuthorizationString getAuthorizationForToday(GroupSecretParams groupSecretParams) throws IOException;
|
||||||
|
}
|
|
@ -2,6 +2,8 @@ package org.asamk.signal.manager.helper;
|
||||||
|
|
||||||
import com.google.protobuf.InvalidProtocolBufferException;
|
import com.google.protobuf.InvalidProtocolBufferException;
|
||||||
|
|
||||||
|
import org.asamk.signal.storage.groups.GroupInfoV2;
|
||||||
|
import org.asamk.signal.util.IOUtils;
|
||||||
import org.signal.storageservice.protos.groups.GroupChange;
|
import org.signal.storageservice.protos.groups.GroupChange;
|
||||||
import org.signal.storageservice.protos.groups.Member;
|
import org.signal.storageservice.protos.groups.Member;
|
||||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||||
|
@ -10,16 +12,24 @@ import org.signal.zkgroup.VerificationFailedException;
|
||||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||||
import org.signal.zkgroup.groups.GroupSecretParams;
|
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||||
import org.signal.zkgroup.profiles.ProfileKeyCredential;
|
import org.signal.zkgroup.profiles.ProfileKeyCredential;
|
||||||
|
import org.whispersystems.libsignal.util.Pair;
|
||||||
import org.whispersystems.libsignal.util.guava.Optional;
|
import org.whispersystems.libsignal.util.guava.Optional;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
import org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupCandidate;
|
import org.whispersystems.signalservice.api.groupsv2.GroupCandidate;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
|
||||||
|
import org.whispersystems.signalservice.api.groupsv2.GroupsV2AuthorizationString;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
|
import org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException;
|
||||||
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException;
|
import org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException;
|
||||||
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
|
||||||
|
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
|
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class GroupHelper {
|
public class GroupHelper {
|
||||||
|
@ -32,19 +42,69 @@ public class GroupHelper {
|
||||||
|
|
||||||
private final GroupsV2Operations groupsV2Operations;
|
private final GroupsV2Operations groupsV2Operations;
|
||||||
|
|
||||||
|
private final GroupsV2Api groupsV2Api;
|
||||||
|
|
||||||
|
private final GroupAuthorizationProvider groupAuthorizationProvider;
|
||||||
|
|
||||||
public GroupHelper(
|
public GroupHelper(
|
||||||
final ProfileKeyCredentialProvider profileKeyCredentialProvider,
|
final ProfileKeyCredentialProvider profileKeyCredentialProvider,
|
||||||
final ProfileProvider profileProvider,
|
final ProfileProvider profileProvider,
|
||||||
final SelfAddressProvider selfAddressProvider,
|
final SelfAddressProvider selfAddressProvider,
|
||||||
final GroupsV2Operations groupsV2Operations
|
final GroupsV2Operations groupsV2Operations,
|
||||||
|
final GroupsV2Api groupsV2Api,
|
||||||
|
final GroupAuthorizationProvider groupAuthorizationProvider
|
||||||
) {
|
) {
|
||||||
this.profileKeyCredentialProvider = profileKeyCredentialProvider;
|
this.profileKeyCredentialProvider = profileKeyCredentialProvider;
|
||||||
this.profileProvider = profileProvider;
|
this.profileProvider = profileProvider;
|
||||||
this.selfAddressProvider = selfAddressProvider;
|
this.selfAddressProvider = selfAddressProvider;
|
||||||
this.groupsV2Operations = groupsV2Operations;
|
this.groupsV2Operations = groupsV2Operations;
|
||||||
|
this.groupsV2Api = groupsV2Api;
|
||||||
|
this.groupAuthorizationProvider = groupAuthorizationProvider;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupsV2Operations.NewGroup createGroupV2(
|
public GroupInfoV2 createGroupV2(
|
||||||
|
String name, Collection<SignalServiceAddress> members, String avatarFile
|
||||||
|
) throws IOException {
|
||||||
|
final byte[] avatarBytes = readAvatarBytes(avatarFile);
|
||||||
|
final GroupsV2Operations.NewGroup newGroup = buildNewGroupV2(name, members, avatarBytes);
|
||||||
|
if (newGroup == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final GroupSecretParams groupSecretParams = newGroup.getGroupSecretParams();
|
||||||
|
|
||||||
|
final GroupsV2AuthorizationString groupAuthForToday;
|
||||||
|
final DecryptedGroup decryptedGroup;
|
||||||
|
try {
|
||||||
|
groupAuthForToday = groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams);
|
||||||
|
groupsV2Api.putNewGroup(newGroup, groupAuthForToday);
|
||||||
|
decryptedGroup = groupsV2Api.getGroup(groupSecretParams, groupAuthForToday);
|
||||||
|
} catch (IOException | VerificationFailedException | InvalidGroupStateException e) {
|
||||||
|
System.err.println("Failed to create V2 group: " + e.getMessage());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (decryptedGroup == null) {
|
||||||
|
System.err.println("Failed to create V2 group!");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final byte[] groupId = groupSecretParams.getPublicParams().getGroupIdentifier().serialize();
|
||||||
|
final GroupMasterKey masterKey = groupSecretParams.getMasterKey();
|
||||||
|
GroupInfoV2 g = new GroupInfoV2(groupId, masterKey);
|
||||||
|
g.setGroup(decryptedGroup);
|
||||||
|
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] readAvatarBytes(final String avatarFile) throws IOException {
|
||||||
|
final byte[] avatarBytes;
|
||||||
|
try (InputStream avatar = avatarFile == null ? null : new FileInputStream(avatarFile)) {
|
||||||
|
avatarBytes = avatar == null ? null : IOUtils.readFully(avatar);
|
||||||
|
}
|
||||||
|
return avatarBytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private GroupsV2Operations.NewGroup buildNewGroupV2(
|
||||||
String name, Collection<SignalServiceAddress> members, byte[] avatar
|
String name, Collection<SignalServiceAddress> members, byte[] avatar
|
||||||
) {
|
) {
|
||||||
final ProfileKeyCredential profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential(
|
final ProfileKeyCredential profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential(
|
||||||
|
@ -90,6 +150,80 @@ public class GroupHelper {
|
||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Pair<DecryptedGroup, GroupChange> updateGroupV2(
|
||||||
|
GroupInfoV2 groupInfoV2, String name, String avatarFile
|
||||||
|
) throws IOException {
|
||||||
|
final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
||||||
|
GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
|
||||||
|
|
||||||
|
GroupChange.Actions.Builder change = name != null
|
||||||
|
? groupOperations.createModifyGroupTitle(name)
|
||||||
|
: GroupChange.Actions.newBuilder();
|
||||||
|
|
||||||
|
if (avatarFile != null) {
|
||||||
|
final byte[] avatarBytes = readAvatarBytes(avatarFile);
|
||||||
|
String avatarCdnKey = groupsV2Api.uploadAvatar(avatarBytes,
|
||||||
|
groupSecretParams,
|
||||||
|
groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams));
|
||||||
|
change.setModifyAvatar(GroupChange.Actions.ModifyAvatarAction.newBuilder().setAvatar(avatarCdnKey));
|
||||||
|
}
|
||||||
|
|
||||||
|
final Optional<UUID> uuid = this.selfAddressProvider.getSelfAddress().getUuid();
|
||||||
|
if (uuid.isPresent()) {
|
||||||
|
change.setSourceUuid(UuidUtil.toByteString(uuid.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return commitChange(groupInfoV2, change);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Pair<DecryptedGroup, GroupChange> updateGroupV2(
|
||||||
|
GroupInfoV2 groupInfoV2, Set<SignalServiceAddress> newMembers
|
||||||
|
) throws IOException {
|
||||||
|
final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
||||||
|
GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
|
||||||
|
|
||||||
|
Set<GroupCandidate> candidates = newMembers.stream()
|
||||||
|
.map(member -> new GroupCandidate(member.getUuid().get(),
|
||||||
|
Optional.fromNullable(profileKeyCredentialProvider.getProfileKeyCredential(member))))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
|
final GroupChange.Actions.Builder change = groupOperations.createModifyGroupMembershipChange(candidates,
|
||||||
|
selfAddressProvider.getSelfAddress().getUuid().get());
|
||||||
|
|
||||||
|
final Optional<UUID> uuid = this.selfAddressProvider.getSelfAddress().getUuid();
|
||||||
|
if (uuid.isPresent()) {
|
||||||
|
change.setSourceUuid(UuidUtil.toByteString(uuid.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return commitChange(groupInfoV2, change);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Pair<DecryptedGroup, GroupChange> commitChange(
|
||||||
|
GroupInfoV2 groupInfoV2, GroupChange.Actions.Builder change
|
||||||
|
) throws IOException {
|
||||||
|
final GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
|
||||||
|
final GroupsV2Operations.GroupOperations groupOperations = groupsV2Operations.forGroup(groupSecretParams);
|
||||||
|
final DecryptedGroup previousGroupState = groupInfoV2.getGroup();
|
||||||
|
final int nextRevision = previousGroupState.getRevision() + 1;
|
||||||
|
final GroupChange.Actions changeActions = change.setRevision(nextRevision).build();
|
||||||
|
final DecryptedGroupChange decryptedChange;
|
||||||
|
final DecryptedGroup decryptedGroupState;
|
||||||
|
|
||||||
|
try {
|
||||||
|
decryptedChange = groupOperations.decryptChange(changeActions,
|
||||||
|
selfAddressProvider.getSelfAddress().getUuid().get());
|
||||||
|
decryptedGroupState = DecryptedGroupUtil.apply(previousGroupState, decryptedChange);
|
||||||
|
} catch (VerificationFailedException | InvalidGroupStateException | NotAbleToApplyGroupV2ChangeException e) {
|
||||||
|
throw new IOException(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupChange signedGroupChange = groupsV2Api.patchGroup(change.build(),
|
||||||
|
groupAuthorizationProvider.getAuthorizationForToday(groupSecretParams),
|
||||||
|
Optional.absent());
|
||||||
|
|
||||||
|
return new Pair<>(decryptedGroupState, signedGroupChange);
|
||||||
|
}
|
||||||
|
|
||||||
public DecryptedGroup getUpdatedDecryptedGroup(
|
public DecryptedGroup getUpdatedDecryptedGroup(
|
||||||
DecryptedGroup group, byte[] signedGroupChange, GroupMasterKey groupMasterKey
|
DecryptedGroup group, byte[] signedGroupChange, GroupMasterKey groupMasterKey
|
||||||
) {
|
) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue