Search in sources :

Example 1 with GroupMasterKey

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

the class GroupDatabase method migrateToV2.

/**
 * Migrates a V1 group to a V2 group.
 *
 * @param decryptedGroup The state that represents the group on the server. This will be used to
 *                       determine if we need to save our old membership list and stuff.
 */
@NonNull
public GroupId.V2 migrateToV2(long threadId, @NonNull GroupId.V1 groupIdV1, @NonNull DecryptedGroup decryptedGroup) {
    SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
    GroupId.V2 groupIdV2 = groupIdV1.deriveV2MigrationGroupId();
    GroupMasterKey groupMasterKey = groupIdV1.deriveV2MigrationMasterKey();
    db.beginTransaction();
    try {
        GroupRecord record = getGroup(groupIdV1).get();
        ContentValues contentValues = new ContentValues();
        contentValues.put(GROUP_ID, groupIdV2.toString());
        contentValues.put(V2_MASTER_KEY, groupMasterKey.serialize());
        contentValues.put(DISTRIBUTION_ID, DistributionId.create().toString());
        contentValues.putNull(EXPECTED_V2_ID);
        List<RecipientId> newMembers = uuidsToRecipientIds(DecryptedGroupUtil.membersToUuidList(decryptedGroup.getMembersList()));
        List<RecipientId> pendingMembers = uuidsToRecipientIds(DecryptedGroupUtil.pendingToUuidList(decryptedGroup.getPendingMembersList()));
        newMembers.addAll(pendingMembers);
        List<RecipientId> droppedMembers = new ArrayList<>(SetUtil.difference(record.getMembers(), newMembers));
        List<RecipientId> unmigratedMembers = Util.concatenatedList(pendingMembers, droppedMembers);
        contentValues.put(UNMIGRATED_V1_MEMBERS, unmigratedMembers.isEmpty() ? null : RecipientId.toSerializedList(unmigratedMembers));
        int updated = db.update(TABLE_NAME, contentValues, GROUP_ID + " = ?", SqlUtil.buildArgs(groupIdV1.toString()));
        if (updated != 1) {
            throw new AssertionError();
        }
        SignalDatabase.recipients().updateGroupId(groupIdV1, groupIdV2);
        update(groupMasterKey, decryptedGroup);
        SignalDatabase.sms().insertGroupV1MigrationEvents(record.getRecipientId(), threadId, new GroupMigrationMembershipChange(pendingMembers, droppedMembers));
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
    return groupIdV2;
}
Also used : ContentValues(android.content.ContentValues) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) GroupMigrationMembershipChange(org.thoughtcrime.securesms.groups.GroupMigrationMembershipChange) ArrayList(java.util.ArrayList) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) SuppressLint(android.annotation.SuppressLint) GroupId(org.thoughtcrime.securesms.groups.GroupId) NonNull(androidx.annotation.NonNull)

Example 2 with GroupMasterKey

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

the class LinkPreviewRepository method fetchGroupLinkPreview.

private static RequestController fetchGroupLinkPreview(@NonNull Context context, @NonNull String groupUrl, @NonNull Callback callback) {
    SignalExecutors.UNBOUNDED.execute(() -> {
        try {
            GroupInviteLinkUrl groupInviteLinkUrl = GroupInviteLinkUrl.fromUri(groupUrl);
            if (groupInviteLinkUrl == null) {
                throw new AssertionError();
            }
            GroupMasterKey groupMasterKey = groupInviteLinkUrl.getGroupMasterKey();
            GroupId.V2 groupId = GroupId.v2(groupMasterKey);
            Optional<GroupDatabase.GroupRecord> group = SignalDatabase.groups().getGroup(groupId);
            if (group.isPresent()) {
                Log.i(TAG, "Creating preview for locally available group");
                GroupDatabase.GroupRecord groupRecord = group.get();
                String title = groupRecord.getTitle();
                int memberCount = groupRecord.getMembers().size();
                String description = getMemberCountDescription(context, memberCount);
                Optional<Attachment> thumbnail = Optional.absent();
                if (AvatarHelper.hasAvatar(context, groupRecord.getRecipientId())) {
                    Recipient recipient = Recipient.resolved(groupRecord.getRecipientId());
                    Bitmap bitmap = AvatarUtil.loadIconBitmapSquareNoCache(context, recipient, 512, 512);
                    thumbnail = bitmapToAttachment(bitmap, Bitmap.CompressFormat.WEBP, MediaUtil.IMAGE_WEBP);
                }
                callback.onSuccess(new LinkPreview(groupUrl, title, description, 0, thumbnail));
            } else {
                Log.i(TAG, "Group is not locally available for preview generation, fetching from server");
                DecryptedGroupJoinInfo joinInfo = GroupManager.getGroupJoinInfoFromServer(context, groupMasterKey, groupInviteLinkUrl.getPassword());
                String description = getMemberCountDescription(context, joinInfo.getMemberCount());
                Optional<Attachment> thumbnail = Optional.absent();
                byte[] avatarBytes = AvatarGroupsV2DownloadJob.downloadGroupAvatarBytes(context, groupMasterKey, joinInfo.getAvatar());
                if (avatarBytes != null) {
                    Bitmap bitmap = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
                    thumbnail = bitmapToAttachment(bitmap, Bitmap.CompressFormat.WEBP, MediaUtil.IMAGE_WEBP);
                    if (bitmap != null)
                        bitmap.recycle();
                }
                callback.onSuccess(new LinkPreview(groupUrl, joinInfo.getTitle(), description, 0, thumbnail));
            }
        } catch (ExecutionException | InterruptedException | IOException | VerificationFailedException e) {
            Log.w(TAG, "Failed to fetch group link preview.", e);
            callback.onError(Error.PREVIEW_NOT_AVAILABLE);
        } catch (GroupInviteLinkUrl.InvalidGroupLinkException | GroupInviteLinkUrl.UnknownGroupLinkVersionException e) {
            Log.w(TAG, "Bad group link.", e);
            callback.onError(Error.PREVIEW_NOT_AVAILABLE);
        } catch (GroupLinkNotActiveException e) {
            Log.w(TAG, "Group link not active.", e);
            callback.onError(Error.GROUP_LINK_INACTIVE);
        }
    });
    return () -> Log.i(TAG, "Cancelled group link preview fetch -- no effect.");
}
Also used : Attachment(org.thoughtcrime.securesms.attachments.Attachment) UriAttachment(org.thoughtcrime.securesms.attachments.UriAttachment) Bitmap(android.graphics.Bitmap) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) ExecutionException(java.util.concurrent.ExecutionException) DecryptedGroupJoinInfo(org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo) GroupInviteLinkUrl(org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) Recipient(org.thoughtcrime.securesms.recipients.Recipient) IOException(java.io.IOException) GroupId(org.thoughtcrime.securesms.groups.GroupId) VerificationFailedException(org.signal.zkgroup.VerificationFailedException) GroupLinkNotActiveException(org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException)

Example 3 with GroupMasterKey

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

the class GroupsV2Operations_decrypt_groupJoinInfo_Test method setup.

@Before
public void setup() throws InvalidInputException {
    LibSignalLibraryUtil.assumeLibSignalSupportedOnOS();
    TestZkGroupServer server = new TestZkGroupServer();
    ClientZkOperations clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
    GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
    groupOperations = new GroupsV2Operations(clientZkOperations).forGroup(groupSecretParams);
}
Also used : GroupSecretParams(org.signal.zkgroup.groups.GroupSecretParams) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) Before(org.junit.Before)

Example 4 with GroupMasterKey

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

the class ThreadDatabase method applyStorageSyncUpdate.

public void applyStorageSyncUpdate(@NonNull RecipientId recipientId, @NonNull SignalAccountRecord record) {
    SQLiteDatabase db = databaseHelper.getSignalWritableDatabase();
    db.beginTransaction();
    try {
        applyStorageSyncUpdate(recipientId, record.isNoteToSelfArchived(), record.isNoteToSelfForcedUnread());
        ContentValues clearPinnedValues = new ContentValues();
        clearPinnedValues.put(PINNED, 0);
        db.update(TABLE_NAME, clearPinnedValues, null, null);
        int pinnedPosition = 1;
        for (SignalAccountRecord.PinnedConversation pinned : record.getPinnedConversations()) {
            ContentValues pinnedValues = new ContentValues();
            pinnedValues.put(PINNED, pinnedPosition);
            Recipient pinnedRecipient;
            if (pinned.getContact().isPresent()) {
                pinnedRecipient = Recipient.externalPush(pinned.getContact().get());
            } else if (pinned.getGroupV1Id().isPresent()) {
                try {
                    pinnedRecipient = Recipient.externalGroupExact(context, GroupId.v1(pinned.getGroupV1Id().get()));
                } catch (BadGroupIdException e) {
                    Log.w(TAG, "Failed to parse pinned groupV1 ID!", e);
                    pinnedRecipient = null;
                }
            } else if (pinned.getGroupV2MasterKey().isPresent()) {
                try {
                    pinnedRecipient = Recipient.externalGroupExact(context, GroupId.v2(new GroupMasterKey(pinned.getGroupV2MasterKey().get())));
                } catch (InvalidInputException e) {
                    Log.w(TAG, "Failed to parse pinned groupV2 master key!", e);
                    pinnedRecipient = null;
                }
            } else {
                Log.w(TAG, "Empty pinned conversation on the AccountRecord?");
                pinnedRecipient = null;
            }
            if (pinnedRecipient != null) {
                db.update(TABLE_NAME, pinnedValues, RECIPIENT_ID + " = ?", SqlUtil.buildArgs(pinnedRecipient.getId()));
            }
            pinnedPosition++;
        }
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
    notifyConversationListListeners();
}
Also used : ContentValues(android.content.ContentValues) InvalidInputException(org.signal.zkgroup.InvalidInputException) SignalAccountRecord(org.whispersystems.signalservice.api.storage.SignalAccountRecord) Recipient(org.thoughtcrime.securesms.recipients.Recipient) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) BadGroupIdException(org.thoughtcrime.securesms.groups.BadGroupIdException)

Example 5 with GroupMasterKey

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

the class GroupManagerV2 method getUuidCipherTexts.

@WorkerThread
@NonNull
Map<UUID, UuidCiphertext> getUuidCipherTexts(@NonNull GroupId.V2 groupId) {
    GroupDatabase.GroupRecord groupRecord = SignalDatabase.groups().requireGroup(groupId);
    GroupDatabase.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
    GroupMasterKey groupMasterKey = v2GroupProperties.getGroupMasterKey();
    ClientZkGroupCipher clientZkGroupCipher = new ClientZkGroupCipher(GroupSecretParams.deriveFromMasterKey(groupMasterKey));
    List<Recipient> recipients = v2GroupProperties.getMemberRecipients(GroupDatabase.MemberSet.FULL_MEMBERS_INCLUDING_SELF);
    Map<UUID, UuidCiphertext> uuidCipherTexts = new HashMap<>();
    for (Recipient recipient : recipients) {
        uuidCipherTexts.put(recipient.requireServiceId().uuid(), clientZkGroupCipher.encryptUuid(recipient.requireServiceId().uuid()));
    }
    return uuidCipherTexts;
}
Also used : HashMap(java.util.HashMap) UuidCiphertext(org.signal.zkgroup.groups.UuidCiphertext) GroupDatabase(org.thoughtcrime.securesms.database.GroupDatabase) GroupMasterKey(org.signal.zkgroup.groups.GroupMasterKey) Recipient(org.thoughtcrime.securesms.recipients.Recipient) ClientZkGroupCipher(org.signal.zkgroup.groups.ClientZkGroupCipher) UUID(java.util.UUID) WorkerThread(androidx.annotation.WorkerThread) NonNull(androidx.annotation.NonNull)

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