Search in sources :

Example 6 with GroupMasterKey

use of org.signal.zkgroup.groups.GroupMasterKey in project Signal-Android by WhisperSystems.

the class GroupManagerV2 method migrateGroupOnToServer.

@WorkerThread
void migrateGroupOnToServer(@NonNull GroupId.V1 groupIdV1, @NonNull Collection<Recipient> members) throws IOException, MembershipNotSuitableForV2Exception, GroupAlreadyExistsException, GroupChangeFailedException {
    GroupMasterKey groupMasterKey = groupIdV1.deriveV2MigrationMasterKey();
    GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
    GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupIdV1);
    String name = Util.emptyIfNull(groupRecord.getTitle());
    byte[] avatar = groupRecord.hasAvatar() ? AvatarHelper.getAvatarBytes(context, groupRecord.getRecipientId()) : null;
    int messageTimer = Recipient.resolved(groupRecord.getRecipientId()).getExpiresInSeconds();
    Set<RecipientId> memberIds = Stream.of(members).map(Recipient::getId).filterNot(m -> m.equals(Recipient.self().getId())).collect(Collectors.toSet());
    createGroupOnServer(groupSecretParams, name, avatar, memberIds, Member.Role.ADMINISTRATOR, messageTimer);
}
Also used : SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) Arrays(java.util.Arrays) MessageSender(org.thoughtcrime.securesms.sms.MessageSender) NonNull(androidx.annotation.NonNull) GroupChangeUtil(org.whispersystems.signalservice.api.groupsv2.GroupChangeUtil) RequestGroupV2InfoJob(org.thoughtcrime.securesms.jobs.RequestGroupV2InfoJob) ProfileKey(org.signal.zkgroup.profiles.ProfileKey) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) PushGroupSilentUpdateSendJob(org.thoughtcrime.securesms.jobs.PushGroupSilentUpdateSendJob) Member(org.signal.storageservice.protos.groups.Member) DecryptedGroupJoinInfo(org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo) ProfileUploadJob(org.thoughtcrime.securesms.jobs.ProfileUploadJob) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) ByteArrayInputStream(java.io.ByteArrayInputStream) Locale(java.util.Locale) Map(java.util.Map) DecryptedGroupChange(org.signal.storageservice.protos.groups.local.DecryptedGroupChange) GroupExistsException(org.whispersystems.signalservice.internal.push.exceptions.GroupExistsException) Recipient(org.thoughtcrime.securesms.recipients.Recipient) GroupsV2StateProcessor(org.thoughtcrime.securesms.groups.v2.processing.GroupsV2StateProcessor) OutgoingGroupUpdateMessage(org.thoughtcrime.securesms.mms.OutgoingGroupUpdateMessage) InvalidGroupStateException(org.whispersystems.signalservice.api.groupsv2.InvalidGroupStateException) InvalidProtocolBufferException(com.google.protobuf.InvalidProtocolBufferException) NotInGroupException(org.whispersystems.signalservice.internal.push.exceptions.NotInGroupException) GroupCandidateHelper(org.thoughtcrime.securesms.groups.v2.GroupCandidateHelper) GroupCandidate(org.whispersystems.signalservice.api.groupsv2.GroupCandidate) GroupExternalCredential(org.signal.storageservice.protos.groups.GroupExternalCredential) ACI(org.whispersystems.signalservice.api.push.ACI) DecryptedGroupUtil(org.whispersystems.signalservice.api.groupsv2.DecryptedGroupUtil) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) Collection(java.util.Collection) ProfileKeyUtil(org.thoughtcrime.securesms.crypto.ProfileKeyUtil) Set(java.util.Set) DecryptedPendingMember(org.signal.storageservice.protos.groups.local.DecryptedPendingMember) GroupLinkPassword(org.thoughtcrime.securesms.groups.v2.GroupLinkPassword) GroupsV2Operations(org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations) UUID(java.util.UUID) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) ThreadDatabase(org.thoughtcrime.securesms.database.ThreadDatabase) InvalidInputException(org.signal.zkgroup.InvalidInputException) ByteString(com.google.protobuf.ByteString) Objects(java.util.Objects) Log(org.signal.core.util.logging.Log) DecryptedGroup(org.signal.storageservice.protos.groups.local.DecryptedGroup) List(java.util.List) Nullable(androidx.annotation.Nullable) Context(android.content.Context) SignalDatabase(org.thoughtcrime.securesms.database.SignalDatabase) Stream(com.annimon.stream.Stream) Util(org.thoughtcrime.securesms.util.Util) WorkerThread(androidx.annotation.WorkerThread) VerificationFailedException(org.signal.zkgroup.VerificationFailedException) HashMap(java.util.HashMap) AccessControl(org.signal.storageservice.protos.groups.AccessControl) GroupInviteLinkUrl(org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl) GroupPatchNotAcceptedException(org.whispersystems.signalservice.internal.push.exceptions.GroupPatchNotAcceptedException) HashSet(java.util.HashSet) AuthorizationFailedException(org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException) ClientZkGroupCipher(org.signal.zkgroup.groups.ClientZkGroupCipher) NotAbleToApplyGroupV2ChangeException(org.whispersystems.signalservice.api.groupsv2.NotAbleToApplyGroupV2ChangeException) DecryptedRequestingMember(org.signal.storageservice.protos.groups.local.DecryptedRequestingMember) DecryptedMember(org.signal.storageservice.protos.groups.local.DecryptedMember) DecryptedGroupV2Context(org.thoughtcrime.securesms.database.model.databaseprotos.DecryptedGroupV2Context) ConflictException(org.whispersystems.signalservice.api.push.exceptions.ConflictException) GroupChangeReconstruct(org.whispersystems.signalservice.api.groupsv2.GroupChangeReconstruct) Collectors(com.annimon.stream.Collectors) ProfileKeyCredential(org.signal.zkgroup.profiles.ProfileKeyCredential) GroupSecretParams(org.signal.zkgroup.groups.GroupSecretParams) AvatarHelper(org.thoughtcrime.securesms.profiles.AvatarHelper) MmsException(org.thoughtcrime.securesms.mms.MmsException) UuidUtil(org.whispersystems.signalservice.api.util.UuidUtil) IOException(java.io.IOException) GroupsV2Api(org.whispersystems.signalservice.api.groupsv2.GroupsV2Api) Optional(org.whispersystems.libsignal.util.guava.Optional) GroupLinkNotActiveException(org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException) UuidCiphertext(org.signal.zkgroup.groups.UuidCiphertext) Closeable(java.io.Closeable) GroupChange(org.signal.storageservice.protos.groups.GroupChange) Collections(java.util.Collections) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) GroupSecretParams(org.signal.zkgroup.groups.GroupSecretParams) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) Recipient(org.thoughtcrime.securesms.recipients.Recipient) ByteString(com.google.protobuf.ByteString) WorkerThread(androidx.annotation.WorkerThread)

Example 7 with GroupMasterKey

use of org.signal.zkgroup.groups.GroupMasterKey in project Signal-Android by WhisperSystems.

the class GroupManagerV2 method getGroupExternalCredential.

@WorkerThread
@NonNull
GroupExternalCredential getGroupExternalCredential(@NonNull GroupId.V2 groupId) throws IOException, VerificationFailedException {
    GroupMasterKey groupMasterKey = SignalDatabase.groups().requireGroup(groupId).requireV2GroupProperties().getGroupMasterKey();
    GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
    return groupsV2Api.getGroupExternalCredential(authorization.getAuthorizationForToday(selfAci, groupSecretParams));
}
Also used : GroupSecretParams(org.signal.zkgroup.groups.GroupSecretParams) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) WorkerThread(androidx.annotation.WorkerThread) NonNull(androidx.annotation.NonNull)

Example 8 with GroupMasterKey

use of org.signal.zkgroup.groups.GroupMasterKey in project Signal-Android by WhisperSystems.

the class GroupsV1MigrationUtil method migrate.

public static void migrate(@NonNull Context context, @NonNull RecipientId recipientId, boolean forced) throws IOException, RetryLaterException, GroupChangeBusyException, InvalidMigrationStateException {
    Recipient groupRecipient = Recipient.resolved(recipientId);
    Long threadId = SignalDatabase.threads().getThreadIdFor(recipientId);
    GroupDatabase groupDatabase = SignalDatabase.groups();
    if (threadId == null) {
        Log.w(TAG, "No thread found!");
        throw new InvalidMigrationStateException();
    }
    if (!groupRecipient.isPushV1Group()) {
        Log.w(TAG, "Not a V1 group!");
        throw new InvalidMigrationStateException();
    }
    if (groupRecipient.getParticipants().size() > FeatureFlags.groupLimits().getHardLimit()) {
        Log.w(TAG, "Too many members! Size: " + groupRecipient.getParticipants().size());
        throw new InvalidMigrationStateException();
    }
    GroupId.V1 gv1Id = groupRecipient.requireGroupId().requireV1();
    GroupId.V2 gv2Id = gv1Id.deriveV2MigrationGroupId();
    GroupMasterKey gv2MasterKey = gv1Id.deriveV2MigrationMasterKey();
    boolean newlyCreated = false;
    if (groupDatabase.groupExists(gv2Id)) {
        Log.w(TAG, "We already have a V2 group for this V1 group! Must have been added before we were migration-capable.");
        throw new InvalidMigrationStateException();
    }
    if (!groupRecipient.isActiveGroup()) {
        Log.w(TAG, "Group is inactive! Can't migrate.");
        throw new InvalidMigrationStateException();
    }
    switch(GroupManager.v2GroupStatus(context, gv2MasterKey)) {
        case DOES_NOT_EXIST:
            Log.i(TAG, "Group does not exist on the service.");
            if (!groupRecipient.isProfileSharing()) {
                Log.w(TAG, "Profile sharing is disabled! Can't migrate.");
                throw new InvalidMigrationStateException();
            }
            if (!forced && SignalStore.internalValues().disableGv1AutoMigrateInitiation()) {
                Log.w(TAG, "Auto migration initiation has been disabled! Skipping.");
                throw new InvalidMigrationStateException();
            }
            List<Recipient> registeredMembers = RecipientUtil.getEligibleForSending(groupRecipient.getParticipants());
            if (RecipientUtil.ensureUuidsAreAvailable(context, registeredMembers)) {
                Log.i(TAG, "Newly-discovered UUIDs. Getting fresh recipients.");
                registeredMembers = Stream.of(registeredMembers).map(Recipient::fresh).toList();
            }
            List<Recipient> possibleMembers = forced ? getMigratableManualMigrationMembers(registeredMembers) : getMigratableAutoMigrationMembers(registeredMembers);
            if (!forced && !groupRecipient.hasName()) {
                Log.w(TAG, "Group has no name. Skipping auto-migration.");
                throw new InvalidMigrationStateException();
            }
            if (!forced && possibleMembers.size() != registeredMembers.size()) {
                Log.w(TAG, "Not allowed to invite or leave registered users behind in an auto-migration! Skipping.");
                throw new InvalidMigrationStateException();
            }
            Log.i(TAG, "Attempting to create group.");
            try {
                GroupManager.migrateGroupToServer(context, gv1Id, possibleMembers);
                newlyCreated = true;
                Log.i(TAG, "Successfully created!");
            } catch (GroupChangeFailedException e) {
                Log.w(TAG, "Failed to migrate group. Retrying.", e);
                throw new RetryLaterException();
            } catch (MembershipNotSuitableForV2Exception e) {
                Log.w(TAG, "Failed to migrate job due to the membership not yet being suitable for GV2. Aborting.", e);
                return;
            } catch (GroupAlreadyExistsException e) {
                Log.w(TAG, "Someone else created the group while we were trying to do the same! It exists now. Continuing on.", e);
            }
            break;
        case NOT_A_MEMBER:
            Log.w(TAG, "The migrated group already exists, but we are not a member. Doing a local leave.");
            handleLeftBehind(context, gv1Id, groupRecipient, threadId);
            return;
        case FULL_OR_PENDING_MEMBER:
            Log.w(TAG, "The migrated group already exists, and we're in it. Continuing on.");
            break;
        default:
            throw new AssertionError();
    }
    Log.i(TAG, "Migrating local group " + gv1Id + " to " + gv2Id);
    DecryptedGroup decryptedGroup = performLocalMigration(context, gv1Id, threadId, groupRecipient);
    if (newlyCreated && decryptedGroup != null && !SignalStore.internalValues().disableGv1AutoMigrateNotification()) {
        Log.i(TAG, "Sending no-op update to notify others.");
        GroupManager.sendNoopUpdate(context, gv2MasterKey, decryptedGroup);
    }
}
Also used : Recipient(org.thoughtcrime.securesms.recipients.Recipient) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) RetryLaterException(org.thoughtcrime.securesms.transport.RetryLaterException) DecryptedGroup(org.signal.storageservice.protos.groups.local.DecryptedGroup)

Example 9 with GroupMasterKey

use of org.signal.zkgroup.groups.GroupMasterKey in project Signal-Android by WhisperSystems.

the class GroupIdTest method can_create_for_gv2_from_GroupMasterKey.

@Test
public void can_create_for_gv2_from_GroupMasterKey() throws IOException, InvalidInputException {
    assumeLibSignalSupportedOnOS();
    GroupId.V2 groupId = GroupId.v2(new GroupMasterKey(Hex.fromStringCondensed("0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef")));
    assertEquals("__signal_group__v2__!8c4a5ec277691282f64b965b1b9affc0285380c993c413f7560967d502dcf2e6", groupId.toString());
    assertFalse(groupId.isMms());
    assertFalse(groupId.isV1());
    assertTrue(groupId.isV2());
    assertTrue(groupId.isPush());
}
Also used : GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) Test(org.junit.Test)

Example 10 with GroupMasterKey

use of org.signal.zkgroup.groups.GroupMasterKey in project Signal-Android by WhisperSystems.

the class GroupId_v1_v2_migration_derivation_Test method deriveMigrationV2MasterKey.

@Test
public void deriveMigrationV2MasterKey() {
    GroupId.V1 groupV1Id = GroupId.v1orThrow(Hex.fromStringOrThrow(inputV1GroupId));
    GroupMasterKey migratedGroupMasterKey = groupV1Id.deriveV2MigrationMasterKey();
    assertEquals(expectedMasterKey, Hex.toStringCondensed(migratedGroupMasterKey.serialize()));
}
Also used : GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) Test(org.junit.Test)

Aggregations

GroupMasterKey (org.signal.zkgroup.groups.GroupMasterKey)13 InvalidInputException (org.signal.zkgroup.InvalidInputException)5 Recipient (org.thoughtcrime.securesms.recipients.Recipient)5 NonNull (androidx.annotation.NonNull)4 GroupDatabase (org.thoughtcrime.securesms.database.GroupDatabase)4 WorkerThread (androidx.annotation.WorkerThread)3 IOException (java.io.IOException)3 GroupSecretParams (org.signal.zkgroup.groups.GroupSecretParams)3 ContentValues (android.content.ContentValues)2 Nullable (androidx.annotation.Nullable)2 ByteString (com.google.protobuf.ByteString)2 HashMap (java.util.HashMap)2 UUID (java.util.UUID)2 Test (org.junit.Test)2 DecryptedGroup (org.signal.storageservice.protos.groups.local.DecryptedGroup)2 DecryptedGroupJoinInfo (org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo)2 ClientZkGroupCipher (org.signal.zkgroup.groups.ClientZkGroupCipher)2 UuidCiphertext (org.signal.zkgroup.groups.UuidCiphertext)2 GroupId (org.thoughtcrime.securesms.groups.GroupId)2 SuppressLint (android.annotation.SuppressLint)1