mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-27 04:04:43 +01:00
Verify group ids on peer-to-peer group changes.
This commit is contained in:
committed by
Greyson Parrelli
parent
878900c09c
commit
0913b84657
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.groupsv2
|
||||
|
||||
import org.signal.libsignal.zkgroup.groups.GroupIdentifier
|
||||
|
||||
/**
|
||||
* Details what verification should take place when decrypting a group change.
|
||||
*
|
||||
* @param verify Should perform group change verification during decryption
|
||||
* @param groupId The id this change should apply to and will be verified is set in change payload
|
||||
*/
|
||||
class DecryptChangeVerificationMode private constructor(
|
||||
@get:JvmName("verify") val verify: Boolean,
|
||||
@get:JvmName("groupId") val groupId: GroupIdentifier?
|
||||
) {
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Use when the changes are already trusted. This would be during group creation or when fetching
|
||||
* group changes directly from the server.
|
||||
*/
|
||||
@JvmStatic
|
||||
@get:JvmName("alreadyTrusted")
|
||||
val alreadyTrusted by lazy { DecryptChangeVerificationMode(verify = false, groupId = null) }
|
||||
|
||||
/**
|
||||
* Use when the changes are from an untrusted source like a P2P message of a group change. This
|
||||
* will verify the signature and group id match.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun verify(groupId: GroupIdentifier): DecryptChangeVerificationMode {
|
||||
return DecryptChangeVerificationMode(verify = true, groupId = groupId)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -124,7 +124,7 @@ public class GroupsV2Api {
|
||||
|
||||
for (GroupChanges.GroupChangeState change : group.getGroupChanges().groupChanges) {
|
||||
DecryptedGroup decryptedGroup = change.groupState != null ? groupOperations.decryptGroup(change.groupState) : null;
|
||||
DecryptedGroupChange decryptedChange = change.groupChange != null ? groupOperations.decryptChange(change.groupChange, false).orElse(null) : null;
|
||||
DecryptedGroupChange decryptedChange = change.groupChange != null ? groupOperations.decryptChange(change.groupChange, DecryptChangeVerificationMode.alreadyTrusted()).orElse(null) : null;
|
||||
|
||||
result.add(new DecryptedGroupChangeLog(decryptedGroup, decryptedChange));
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import org.signal.libsignal.zkgroup.ServerPublicParams;
|
||||
import org.signal.libsignal.zkgroup.VerificationFailedException;
|
||||
import org.signal.libsignal.zkgroup.auth.ClientZkAuthOperations;
|
||||
import org.signal.libsignal.zkgroup.groups.ClientZkGroupCipher;
|
||||
import org.signal.libsignal.zkgroup.groups.GroupIdentifier;
|
||||
import org.signal.libsignal.zkgroup.groups.GroupSecretParams;
|
||||
import org.signal.libsignal.zkgroup.groups.ProfileKeyCiphertext;
|
||||
import org.signal.libsignal.zkgroup.groups.UuidCiphertext;
|
||||
@@ -45,6 +46,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
import java.io.IOException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
@@ -486,14 +488,13 @@ public final class GroupsV2Operations {
|
||||
}
|
||||
|
||||
/**
|
||||
* @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.
|
||||
* @param verification 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#empty()} if the epoch for the change is higher that this code can decrypt.
|
||||
*/
|
||||
public Optional<DecryptedGroupChange> decryptChange(GroupChange groupChange, boolean verifySignature)
|
||||
public Optional<DecryptedGroupChange> decryptChange(GroupChange groupChange, @Nonnull DecryptChangeVerificationMode verification)
|
||||
throws IOException, VerificationFailedException, InvalidGroupStateException
|
||||
{
|
||||
if (groupChange.changeEpoch > HIGHEST_KNOWN_EPOCH) {
|
||||
@@ -501,7 +502,14 @@ public final class GroupsV2Operations {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
GroupChange.Actions actions = verifySignature ? getVerifiedActions(groupChange) : getActions(groupChange);
|
||||
GroupChange.Actions actions = verification.verify() ? getVerifiedActions(groupChange) : getActions(groupChange);
|
||||
|
||||
if (verification.verify()) {
|
||||
GroupIdentifier groupId = verification.groupId();
|
||||
if (groupId == null || !Arrays.equals(groupId.serialize(), actions.groupId.toByteArray())) {
|
||||
throw new VerificationFailedException("Invalid group id");
|
||||
}
|
||||
}
|
||||
|
||||
return Optional.of(decryptChange(actions));
|
||||
}
|
||||
|
||||
@@ -75,6 +75,7 @@ message DecryptedGroup {
|
||||
// Keep field numbers in step
|
||||
message DecryptedGroupChange {
|
||||
bytes editorServiceIdBytes = 1;
|
||||
reserved 25; // groupId used only during verification + decrypt, only provided by server
|
||||
uint32 revision = 2;
|
||||
repeated DecryptedMember newMembers = 3;
|
||||
repeated bytes deleteMembers = 4;
|
||||
|
||||
@@ -183,6 +183,7 @@ message GroupChange {
|
||||
}
|
||||
|
||||
bytes sourceServiceId = 1;
|
||||
bytes groupId = 25; // Only set when receiving from server
|
||||
uint32 revision = 2;
|
||||
repeated AddMemberAction addMembers = 3;
|
||||
repeated DeleteMemberAction deleteMembers = 4;
|
||||
|
||||
Reference in New Issue
Block a user