Search in sources :

Example 6 with AttachmentSecret

use of org.thoughtcrime.securesms.crypto.AttachmentSecret in project Signal-Android by WhisperSystems.

the class SignalGlideComponents method registerComponents.

@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
    AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
    byte[] secret = attachmentSecret.getModernKey();
    registry.prepend(File.class, File.class, UnitModelLoader.Factory.getInstance());
    registry.prepend(InputStream.class, new EncryptedCacheEncoder(secret, glide.getArrayPool()));
    registry.prepend(Bitmap.class, new EncryptedBitmapResourceEncoder(secret));
    registry.prepend(File.class, Bitmap.class, new EncryptedCacheDecoder<>(secret, new StreamBitmapDecoder(new Downsampler(registry.getImageHeaderParsers(), context.getResources().getDisplayMetrics(), glide.getBitmapPool(), glide.getArrayPool()), glide.getArrayPool())));
    registry.prepend(GifDrawable.class, new EncryptedGifDrawableResourceEncoder(secret));
    registry.prepend(File.class, GifDrawable.class, new EncryptedCacheDecoder<>(secret, new StreamGifDecoder(registry.getImageHeaderParsers(), new ByteBufferGifDecoder(context, registry.getImageHeaderParsers(), glide.getBitmapPool(), glide.getArrayPool()), glide.getArrayPool())));
    ApngBufferCacheDecoder apngBufferCacheDecoder = new ApngBufferCacheDecoder();
    ApngStreamCacheDecoder apngStreamCacheDecoder = new ApngStreamCacheDecoder(apngBufferCacheDecoder);
    registry.prepend(InputStream.class, APNGDecoder.class, apngStreamCacheDecoder);
    registry.prepend(ByteBuffer.class, APNGDecoder.class, apngBufferCacheDecoder);
    registry.prepend(APNGDecoder.class, new EncryptedApngCacheEncoder(secret));
    registry.prepend(File.class, APNGDecoder.class, new EncryptedCacheDecoder<>(secret, apngStreamCacheDecoder));
    registry.register(APNGDecoder.class, Drawable.class, new ApngFrameDrawableTranscoder());
    registry.prepend(BlurHash.class, Bitmap.class, new BlurHashResourceDecoder());
    registry.prepend(StoryTextPostModel.class, Bitmap.class, new StoryTextPostModel.Decoder());
    registry.append(StoryTextPostModel.class, StoryTextPostModel.class, UnitModelLoader.Factory.getInstance());
    registry.append(ConversationShortcutPhoto.class, Bitmap.class, new ConversationShortcutPhoto.Loader.Factory(context));
    registry.append(ContactPhoto.class, InputStream.class, new ContactPhotoLoader.Factory(context));
    registry.append(DecryptableUri.class, InputStream.class, new DecryptableStreamUriLoader.Factory(context));
    registry.append(AttachmentModel.class, InputStream.class, new AttachmentStreamUriLoader.Factory());
    registry.append(ChunkedImageUrl.class, InputStream.class, new ChunkedImageUrlLoader.Factory());
    registry.append(StickerRemoteUri.class, InputStream.class, new StickerRemoteUriLoader.Factory());
    registry.append(BlurHash.class, BlurHash.class, new BlurHashModelLoader.Factory());
    registry.append(Badge.class, InputStream.class, BadgeLoader.createFactory());
    registry.append(GiftBadgeModel.class, InputStream.class, GiftBadgeModel.createFactory());
    registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory());
}
Also used : OkHttpUrlLoader(org.thoughtcrime.securesms.glide.OkHttpUrlLoader) BlurHashModelLoader(org.thoughtcrime.securesms.blurhash.BlurHashModelLoader) ApngStreamCacheDecoder(org.thoughtcrime.securesms.glide.cache.ApngStreamCacheDecoder) Downsampler(com.bumptech.glide.load.resource.bitmap.Downsampler) BadgeLoader(org.thoughtcrime.securesms.glide.BadgeLoader) ChunkedImageUrlLoader(org.thoughtcrime.securesms.glide.ChunkedImageUrlLoader) ContactPhotoLoader(org.thoughtcrime.securesms.glide.ContactPhotoLoader) StickerRemoteUriLoader(org.thoughtcrime.securesms.stickers.StickerRemoteUriLoader) UnitModelLoader(com.bumptech.glide.load.model.UnitModelLoader) OkHttpUrlLoader(org.thoughtcrime.securesms.glide.OkHttpUrlLoader) BlurHashModelLoader(org.thoughtcrime.securesms.blurhash.BlurHashModelLoader) StreamGifDecoder(com.bumptech.glide.load.resource.gif.StreamGifDecoder) EncryptedApngCacheEncoder(org.thoughtcrime.securesms.glide.cache.EncryptedApngCacheEncoder) EncryptedCacheEncoder(org.thoughtcrime.securesms.glide.cache.EncryptedCacheEncoder) StreamBitmapDecoder(com.bumptech.glide.load.resource.bitmap.StreamBitmapDecoder) StoryTextPostModel(org.thoughtcrime.securesms.stories.StoryTextPostModel) ChunkedImageUrlLoader(org.thoughtcrime.securesms.glide.ChunkedImageUrlLoader) ApngBufferCacheDecoder(org.thoughtcrime.securesms.glide.cache.ApngBufferCacheDecoder) ApngFrameDrawableTranscoder(org.thoughtcrime.securesms.glide.cache.ApngFrameDrawableTranscoder) ByteBufferGifDecoder(com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder) StickerRemoteUriLoader(org.thoughtcrime.securesms.stickers.StickerRemoteUriLoader) EncryptedBitmapResourceEncoder(org.thoughtcrime.securesms.glide.cache.EncryptedBitmapResourceEncoder) EncryptedGifDrawableResourceEncoder(org.thoughtcrime.securesms.glide.cache.EncryptedGifDrawableResourceEncoder) BlurHashResourceDecoder(org.thoughtcrime.securesms.blurhash.BlurHashResourceDecoder) ContactPhotoLoader(org.thoughtcrime.securesms.glide.ContactPhotoLoader) AttachmentSecret(org.thoughtcrime.securesms.crypto.AttachmentSecret)

Example 7 with AttachmentSecret

use of org.thoughtcrime.securesms.crypto.AttachmentSecret in project Signal-Android by WhisperSystems.

the class BlobProvider method writeBlobSpecToDiskAsync.

@WorkerThread
@NonNull
private synchronized Uri writeBlobSpecToDiskAsync(@NonNull Context context, @NonNull BlobSpec blobSpec, @Nullable SuccessListener successListener, @Nullable ErrorListener errorListener) throws IOException {
    AttachmentSecret attachmentSecret = AttachmentSecretProvider.getInstance(context).getOrCreateAttachmentSecret();
    String directory = getDirectory(blobSpec.getStorageType());
    File outputFile = new File(getOrCreateDirectory(context, directory), buildFileName(blobSpec.id));
    OutputStream outputStream = ModernEncryptingPartOutputStream.createFor(attachmentSecret, outputFile, true).second;
    SignalExecutors.UNBOUNDED.execute(() -> {
        try {
            StreamUtil.copy(blobSpec.getData(), outputStream);
            if (successListener != null) {
                successListener.onSuccess();
            }
        } catch (IOException e) {
            Log.w(TAG, "Error during write!", e);
            if (errorListener != null) {
                errorListener.onError(e);
            }
        }
    });
    return buildUri(blobSpec);
}
Also used : OutputStream(java.io.OutputStream) ModernEncryptingPartOutputStream(org.thoughtcrime.securesms.crypto.ModernEncryptingPartOutputStream) IOException(java.io.IOException) AttachmentSecret(org.thoughtcrime.securesms.crypto.AttachmentSecret) File(java.io.File) WorkerThread(androidx.annotation.WorkerThread) NonNull(androidx.annotation.NonNull)

Example 8 with AttachmentSecret

use of org.thoughtcrime.securesms.crypto.AttachmentSecret in project Signal-Android by WhisperSystems.

the class ClassicOpenHelper method onApplicationLevelUpgrade.

public void onApplicationLevelUpgrade(Context context, MasterSecret masterSecret, int fromVersion, LegacyMigrationJob.DatabaseUpgradeListener listener) {
    SQLiteDatabase db = getWritableDatabase();
    db.beginTransaction();
    if (fromVersion < LegacyMigrationJob.NO_MORE_KEY_EXCHANGE_PREFIX_VERSION) {
        String KEY_EXCHANGE = "?TextSecureKeyExchange";
        String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd";
        String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs";
        int ROW_LIMIT = 500;
        MasterCipher masterCipher = new MasterCipher(masterSecret);
        int smsCount = 0;
        int threadCount = 0;
        int skip = 0;
        Cursor cursor = db.query("sms", new String[] { "COUNT(*)" }, "type & " + 0x80000000 + " != 0", null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            smsCount = cursor.getInt(0);
            cursor.close();
        }
        cursor = db.query("thread", new String[] { "COUNT(*)" }, "snippet_type & " + 0x80000000 + " != 0", null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            threadCount = cursor.getInt(0);
            cursor.close();
        }
        Cursor smsCursor = null;
        Log.i(TAG, "Upgrade count: " + (smsCount + threadCount));
        do {
            Log.i(TAG, "Looping SMS cursor...");
            if (smsCursor != null)
                smsCursor.close();
            smsCursor = db.query("sms", new String[] { "_id", "type", "body" }, "type & " + 0x80000000 + " != 0", null, null, null, "_id", skip + "," + ROW_LIMIT);
            while (smsCursor != null && smsCursor.moveToNext()) {
                listener.setProgress(smsCursor.getPosition() + skip, smsCount + threadCount);
                try {
                    String body = masterCipher.decryptBody(smsCursor.getString(smsCursor.getColumnIndexOrThrow("body")));
                    long type = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("type"));
                    long id = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("_id"));
                    if (body.startsWith(KEY_EXCHANGE)) {
                        body = body.substring(KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= 0x8000;
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    } else if (body.startsWith(PROCESSED_KEY_EXCHANGE)) {
                        body = body.substring(PROCESSED_KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= (0x8000 | 0x2000);
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    } else if (body.startsWith(STALE_KEY_EXCHANGE)) {
                        body = body.substring(STALE_KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= (0x8000 | 0x4000);
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    }
                } catch (InvalidMessageException e) {
                    Log.w(TAG, e);
                }
            }
            skip += ROW_LIMIT;
        } while (smsCursor != null && smsCursor.getCount() > 0);
        Cursor threadCursor = null;
        skip = 0;
        do {
            Log.i(TAG, "Looping thread cursor...");
            if (threadCursor != null)
                threadCursor.close();
            threadCursor = db.query("thread", new String[] { "_id", "snippet_type", "snippet" }, "snippet_type & " + 0x80000000 + " != 0", null, null, null, "_id", skip + "," + ROW_LIMIT);
            while (threadCursor != null && threadCursor.moveToNext()) {
                listener.setProgress(smsCount + threadCursor.getPosition(), smsCount + threadCount);
                try {
                    String snippet = threadCursor.getString(threadCursor.getColumnIndexOrThrow("snippet"));
                    long snippetType = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("snippet_type"));
                    long id = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id"));
                    if (!TextUtils.isEmpty(snippet)) {
                        snippet = masterCipher.decryptBody(snippet);
                    }
                    if (snippet.startsWith(KEY_EXCHANGE)) {
                        snippet = snippet.substring(KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= 0x8000;
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    } else if (snippet.startsWith(PROCESSED_KEY_EXCHANGE)) {
                        snippet = snippet.substring(PROCESSED_KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= (0x8000 | 0x2000);
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    } else if (snippet.startsWith(STALE_KEY_EXCHANGE)) {
                        snippet = snippet.substring(STALE_KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= (0x8000 | 0x4000);
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    }
                } catch (InvalidMessageException e) {
                    Log.w(TAG, e);
                }
            }
            skip += ROW_LIMIT;
        } while (threadCursor != null && threadCursor.getCount() > 0);
        if (smsCursor != null)
            smsCursor.close();
        if (threadCursor != null)
            threadCursor.close();
    }
    if (fromVersion < LegacyMigrationJob.MMS_BODY_VERSION) {
        Log.i(TAG, "Update MMS bodies...");
        MasterCipher masterCipher = new MasterCipher(masterSecret);
        Cursor mmsCursor = db.query("mms", new String[] { "_id" }, "msg_box & " + 0x80000000L + " != 0", null, null, null, null);
        Log.i(TAG, "Got MMS rows: " + (mmsCursor == null ? "null" : mmsCursor.getCount()));
        while (mmsCursor != null && mmsCursor.moveToNext()) {
            listener.setProgress(mmsCursor.getPosition(), mmsCursor.getCount());
            long mmsId = mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow("_id"));
            String body = null;
            int partCount = 0;
            Cursor partCursor = db.query("part", new String[] { "_id", "ct", "_data", "encrypted" }, "mid = ?", new String[] { mmsId + "" }, null, null, null);
            while (partCursor != null && partCursor.moveToNext()) {
                String contentType = partCursor.getString(partCursor.getColumnIndexOrThrow("ct"));
                if (MediaUtil.isTextType(contentType)) {
                    try {
                        long partId = partCursor.getLong(partCursor.getColumnIndexOrThrow("_id"));
                        String dataLocation = partCursor.getString(partCursor.getColumnIndexOrThrow("_data"));
                        boolean encrypted = partCursor.getInt(partCursor.getColumnIndexOrThrow("encrypted")) == 1;
                        File dataFile = new File(dataLocation);
                        InputStream is;
                        AttachmentSecret attachmentSecret = new AttachmentSecret(masterSecret.getEncryptionKey().getEncoded(), masterSecret.getMacKey().getEncoded(), null);
                        if (encrypted)
                            is = ClassicDecryptingPartInputStream.createFor(attachmentSecret, dataFile);
                        else
                            is = new FileInputStream(dataFile);
                        body = (body == null) ? StreamUtil.readFullyAsString(is) : body + " " + StreamUtil.readFullyAsString(is);
                        // noinspection ResultOfMethodCallIgnored
                        dataFile.delete();
                        db.delete("part", "_id = ?", new String[] { partId + "" });
                    } catch (IOException e) {
                        Log.w(TAG, e);
                    }
                } else if (MediaUtil.isAudioType(contentType) || MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType)) {
                    partCount++;
                }
            }
            if (!TextUtils.isEmpty(body)) {
                body = masterCipher.encryptBody(body);
                db.execSQL("UPDATE mms SET body = ?, part_count = ? WHERE _id = ?", new String[] { body, partCount + "", mmsId + "" });
            } else {
                db.execSQL("UPDATE mms SET part_count = ? WHERE _id = ?", new String[] { partCount + "", mmsId + "" });
            }
            Log.i(TAG, "Updated body: " + body + " and part_count: " + partCount);
        }
    }
    if (fromVersion < LegacyMigrationJob.TOFU_IDENTITIES_VERSION) {
        File sessionDirectory = new File(context.getFilesDir() + File.separator + "sessions");
        if (sessionDirectory.exists() && sessionDirectory.isDirectory()) {
            File[] sessions = sessionDirectory.listFiles();
            if (sessions != null) {
                for (File session : sessions) {
                    String name = session.getName();
                    if (name.matches("[0-9]+")) {
                        long recipientId = Long.parseLong(name);
                        IdentityKey identityKey = null;
                        if (identityKey != null) {
                            MasterCipher masterCipher = new MasterCipher(masterSecret);
                            String identityKeyString = Base64.encodeBytes(identityKey.serialize());
                            String macString = Base64.encodeBytes(masterCipher.getMacFor(recipientId + identityKeyString));
                            db.execSQL("REPLACE INTO identities (recipient, key, mac) VALUES (?, ?, ?)", new String[] { recipientId + "", identityKeyString, macString });
                        }
                    }
                }
            }
        }
    }
    if (fromVersion < LegacyMigrationJob.ASYMMETRIC_MASTER_SECRET_FIX_VERSION) {
        if (!MasterSecretUtil.hasAsymmericMasterSecret(context)) {
            MasterSecretUtil.generateAsymmetricMasterSecret(context, masterSecret);
            MasterCipher masterCipher = new MasterCipher(masterSecret);
            Cursor cursor = null;
            try {
                cursor = db.query(SmsDatabase.TABLE_NAME, new String[] { SmsDatabase.ID, SmsDatabase.BODY, SmsDatabase.TYPE }, SmsDatabase.TYPE + " & ? == 0", new String[] { String.valueOf(SmsDatabase.Types.ENCRYPTION_MASK) }, null, null, null);
                while (cursor.moveToNext()) {
                    long id = cursor.getLong(0);
                    String body = cursor.getString(1);
                    long type = cursor.getLong(2);
                    String encryptedBody = masterCipher.encryptBody(body);
                    ContentValues update = new ContentValues();
                    update.put(SmsDatabase.BODY, encryptedBody);
                    // Inline now deprecated symmetric encryption type
                    update.put(SmsDatabase.TYPE, type | 0x80000000);
                    db.update(SmsDatabase.TABLE_NAME, update, SmsDatabase.ID + " = ?", new String[] { String.valueOf(id) });
                }
            } finally {
                if (cursor != null)
                    cursor.close();
            }
        }
    }
    db.setTransactionSuccessful();
    db.endTransaction();
    // DecryptingQueue.schedulePendingDecrypts(context, masterSecret);
    ApplicationDependencies.getMessageNotifier().updateNotification(context);
}
Also used : ContentValues(android.content.ContentValues) InvalidMessageException(org.signal.libsignal.protocol.InvalidMessageException) IdentityKey(org.signal.libsignal.protocol.IdentityKey) ClassicDecryptingPartInputStream(org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) MasterCipher(org.thoughtcrime.securesms.crypto.MasterCipher) IOException(java.io.IOException) Cursor(android.database.Cursor) FileInputStream(java.io.FileInputStream) SQLiteDatabase(android.database.sqlite.SQLiteDatabase) AttachmentSecret(org.thoughtcrime.securesms.crypto.AttachmentSecret) File(java.io.File)

Example 9 with AttachmentSecret

use of org.thoughtcrime.securesms.crypto.AttachmentSecret in project Signal-Android by signalapp.

the class ClassicOpenHelper method onApplicationLevelUpgrade.

public void onApplicationLevelUpgrade(Context context, MasterSecret masterSecret, int fromVersion, LegacyMigrationJob.DatabaseUpgradeListener listener) {
    SQLiteDatabase db = getWritableDatabase();
    db.beginTransaction();
    if (fromVersion < LegacyMigrationJob.NO_MORE_KEY_EXCHANGE_PREFIX_VERSION) {
        String KEY_EXCHANGE = "?TextSecureKeyExchange";
        String PROCESSED_KEY_EXCHANGE = "?TextSecureKeyExchangd";
        String STALE_KEY_EXCHANGE = "?TextSecureKeyExchangs";
        int ROW_LIMIT = 500;
        MasterCipher masterCipher = new MasterCipher(masterSecret);
        int smsCount = 0;
        int threadCount = 0;
        int skip = 0;
        Cursor cursor = db.query("sms", new String[] { "COUNT(*)" }, "type & " + 0x80000000 + " != 0", null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            smsCount = cursor.getInt(0);
            cursor.close();
        }
        cursor = db.query("thread", new String[] { "COUNT(*)" }, "snippet_type & " + 0x80000000 + " != 0", null, null, null, null);
        if (cursor != null && cursor.moveToFirst()) {
            threadCount = cursor.getInt(0);
            cursor.close();
        }
        Cursor smsCursor = null;
        Log.i(TAG, "Upgrade count: " + (smsCount + threadCount));
        do {
            Log.i(TAG, "Looping SMS cursor...");
            if (smsCursor != null)
                smsCursor.close();
            smsCursor = db.query("sms", new String[] { "_id", "type", "body" }, "type & " + 0x80000000 + " != 0", null, null, null, "_id", skip + "," + ROW_LIMIT);
            while (smsCursor != null && smsCursor.moveToNext()) {
                listener.setProgress(smsCursor.getPosition() + skip, smsCount + threadCount);
                try {
                    String body = masterCipher.decryptBody(smsCursor.getString(smsCursor.getColumnIndexOrThrow("body")));
                    long type = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("type"));
                    long id = smsCursor.getLong(smsCursor.getColumnIndexOrThrow("_id"));
                    if (body.startsWith(KEY_EXCHANGE)) {
                        body = body.substring(KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= 0x8000;
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    } else if (body.startsWith(PROCESSED_KEY_EXCHANGE)) {
                        body = body.substring(PROCESSED_KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= (0x8000 | 0x2000);
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    } else if (body.startsWith(STALE_KEY_EXCHANGE)) {
                        body = body.substring(STALE_KEY_EXCHANGE.length());
                        body = masterCipher.encryptBody(body);
                        type |= (0x8000 | 0x4000);
                        db.execSQL("UPDATE sms SET body = ?, type = ? WHERE _id = ?", new String[] { body, type + "", id + "" });
                    }
                } catch (InvalidMessageException e) {
                    Log.w(TAG, e);
                }
            }
            skip += ROW_LIMIT;
        } while (smsCursor != null && smsCursor.getCount() > 0);
        Cursor threadCursor = null;
        skip = 0;
        do {
            Log.i(TAG, "Looping thread cursor...");
            if (threadCursor != null)
                threadCursor.close();
            threadCursor = db.query("thread", new String[] { "_id", "snippet_type", "snippet" }, "snippet_type & " + 0x80000000 + " != 0", null, null, null, "_id", skip + "," + ROW_LIMIT);
            while (threadCursor != null && threadCursor.moveToNext()) {
                listener.setProgress(smsCount + threadCursor.getPosition(), smsCount + threadCount);
                try {
                    String snippet = threadCursor.getString(threadCursor.getColumnIndexOrThrow("snippet"));
                    long snippetType = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("snippet_type"));
                    long id = threadCursor.getLong(threadCursor.getColumnIndexOrThrow("_id"));
                    if (!TextUtils.isEmpty(snippet)) {
                        snippet = masterCipher.decryptBody(snippet);
                    }
                    if (snippet.startsWith(KEY_EXCHANGE)) {
                        snippet = snippet.substring(KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= 0x8000;
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    } else if (snippet.startsWith(PROCESSED_KEY_EXCHANGE)) {
                        snippet = snippet.substring(PROCESSED_KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= (0x8000 | 0x2000);
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    } else if (snippet.startsWith(STALE_KEY_EXCHANGE)) {
                        snippet = snippet.substring(STALE_KEY_EXCHANGE.length());
                        snippet = masterCipher.encryptBody(snippet);
                        snippetType |= (0x8000 | 0x4000);
                        db.execSQL("UPDATE thread SET snippet = ?, snippet_type = ? WHERE _id = ?", new String[] { snippet, snippetType + "", id + "" });
                    }
                } catch (InvalidMessageException e) {
                    Log.w(TAG, e);
                }
            }
            skip += ROW_LIMIT;
        } while (threadCursor != null && threadCursor.getCount() > 0);
        if (smsCursor != null)
            smsCursor.close();
        if (threadCursor != null)
            threadCursor.close();
    }
    if (fromVersion < LegacyMigrationJob.MMS_BODY_VERSION) {
        Log.i(TAG, "Update MMS bodies...");
        MasterCipher masterCipher = new MasterCipher(masterSecret);
        Cursor mmsCursor = db.query("mms", new String[] { "_id" }, "msg_box & " + 0x80000000L + " != 0", null, null, null, null);
        Log.i(TAG, "Got MMS rows: " + (mmsCursor == null ? "null" : mmsCursor.getCount()));
        while (mmsCursor != null && mmsCursor.moveToNext()) {
            listener.setProgress(mmsCursor.getPosition(), mmsCursor.getCount());
            long mmsId = mmsCursor.getLong(mmsCursor.getColumnIndexOrThrow("_id"));
            String body = null;
            int partCount = 0;
            Cursor partCursor = db.query("part", new String[] { "_id", "ct", "_data", "encrypted" }, "mid = ?", new String[] { mmsId + "" }, null, null, null);
            while (partCursor != null && partCursor.moveToNext()) {
                String contentType = partCursor.getString(partCursor.getColumnIndexOrThrow("ct"));
                if (MediaUtil.isTextType(contentType)) {
                    try {
                        long partId = partCursor.getLong(partCursor.getColumnIndexOrThrow("_id"));
                        String dataLocation = partCursor.getString(partCursor.getColumnIndexOrThrow("_data"));
                        boolean encrypted = partCursor.getInt(partCursor.getColumnIndexOrThrow("encrypted")) == 1;
                        File dataFile = new File(dataLocation);
                        InputStream is;
                        AttachmentSecret attachmentSecret = new AttachmentSecret(masterSecret.getEncryptionKey().getEncoded(), masterSecret.getMacKey().getEncoded(), null);
                        if (encrypted)
                            is = ClassicDecryptingPartInputStream.createFor(attachmentSecret, dataFile);
                        else
                            is = new FileInputStream(dataFile);
                        body = (body == null) ? StreamUtil.readFullyAsString(is) : body + " " + StreamUtil.readFullyAsString(is);
                        // noinspection ResultOfMethodCallIgnored
                        dataFile.delete();
                        db.delete("part", "_id = ?", new String[] { partId + "" });
                    } catch (IOException e) {
                        Log.w(TAG, e);
                    }
                } else if (MediaUtil.isAudioType(contentType) || MediaUtil.isImageType(contentType) || MediaUtil.isVideoType(contentType)) {
                    partCount++;
                }
            }
            if (!TextUtils.isEmpty(body)) {
                body = masterCipher.encryptBody(body);
                db.execSQL("UPDATE mms SET body = ?, part_count = ? WHERE _id = ?", new String[] { body, partCount + "", mmsId + "" });
            } else {
                db.execSQL("UPDATE mms SET part_count = ? WHERE _id = ?", new String[] { partCount + "", mmsId + "" });
            }
            Log.i(TAG, "Updated body: " + body + " and part_count: " + partCount);
        }
    }
    if (fromVersion < LegacyMigrationJob.TOFU_IDENTITIES_VERSION) {
        File sessionDirectory = new File(context.getFilesDir() + File.separator + "sessions");
        if (sessionDirectory.exists() && sessionDirectory.isDirectory()) {
            File[] sessions = sessionDirectory.listFiles();
            if (sessions != null) {
                for (File session : sessions) {
                    String name = session.getName();
                    if (name.matches("[0-9]+")) {
                        long recipientId = Long.parseLong(name);
                        IdentityKey identityKey = null;
                        if (identityKey != null) {
                            MasterCipher masterCipher = new MasterCipher(masterSecret);
                            String identityKeyString = Base64.encodeBytes(identityKey.serialize());
                            String macString = Base64.encodeBytes(masterCipher.getMacFor(recipientId + identityKeyString));
                            db.execSQL("REPLACE INTO identities (recipient, key, mac) VALUES (?, ?, ?)", new String[] { recipientId + "", identityKeyString, macString });
                        }
                    }
                }
            }
        }
    }
    if (fromVersion < LegacyMigrationJob.ASYMMETRIC_MASTER_SECRET_FIX_VERSION) {
        if (!MasterSecretUtil.hasAsymmericMasterSecret(context)) {
            MasterSecretUtil.generateAsymmetricMasterSecret(context, masterSecret);
            MasterCipher masterCipher = new MasterCipher(masterSecret);
            Cursor cursor = null;
            try {
                cursor = db.query(SmsDatabase.TABLE_NAME, new String[] { SmsDatabase.ID, SmsDatabase.BODY, SmsDatabase.TYPE }, SmsDatabase.TYPE + " & ? == 0", new String[] { String.valueOf(SmsDatabase.Types.ENCRYPTION_MASK) }, null, null, null);
                while (cursor.moveToNext()) {
                    long id = cursor.getLong(0);
                    String body = cursor.getString(1);
                    long type = cursor.getLong(2);
                    String encryptedBody = masterCipher.encryptBody(body);
                    ContentValues update = new ContentValues();
                    update.put(SmsDatabase.BODY, encryptedBody);
                    // Inline now deprecated symmetric encryption type
                    update.put(SmsDatabase.TYPE, type | 0x80000000);
                    db.update(SmsDatabase.TABLE_NAME, update, SmsDatabase.ID + " = ?", new String[] { String.valueOf(id) });
                }
            } finally {
                if (cursor != null)
                    cursor.close();
            }
        }
    }
    db.setTransactionSuccessful();
    db.endTransaction();
    // DecryptingQueue.schedulePendingDecrypts(context, masterSecret);
    ApplicationDependencies.getMessageNotifier().updateNotification(context);
}
Also used : ContentValues(android.content.ContentValues) InvalidMessageException(org.signal.libsignal.protocol.InvalidMessageException) IdentityKey(org.signal.libsignal.protocol.IdentityKey) ClassicDecryptingPartInputStream(org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream) FileInputStream(java.io.FileInputStream) InputStream(java.io.InputStream) MasterCipher(org.thoughtcrime.securesms.crypto.MasterCipher) IOException(java.io.IOException) Cursor(android.database.Cursor) FileInputStream(java.io.FileInputStream) SQLiteDatabase(android.database.sqlite.SQLiteDatabase) AttachmentSecret(org.thoughtcrime.securesms.crypto.AttachmentSecret) File(java.io.File)

Example 10 with AttachmentSecret

use of org.thoughtcrime.securesms.crypto.AttachmentSecret in project Signal-Android by signalapp.

the class FullBackupExporter method internalExport.

private static void internalExport(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret, @NonNull SQLiteDatabase input, @NonNull OutputStream fileOutputStream, @NonNull String passphrase, boolean closeOutputStream, @NonNull BackupCancellationSignal cancellationSignal) throws IOException {
    BackupFrameOutputStream outputStream = new BackupFrameOutputStream(fileOutputStream, passphrase);
    int count = 0;
    long estimatedCountOutside = 0L;
    try {
        outputStream.writeDatabaseVersion(input.getVersion());
        count++;
        List<String> tables = exportSchema(input, outputStream);
        count += tables.size() * TABLE_RECORD_COUNT_MULTIPLIER;
        final long estimatedCount = calculateCount(context, input, tables);
        estimatedCountOutside = estimatedCount;
        Stopwatch stopwatch = new Stopwatch("Backup");
        for (String table : tables) {
            throwIfCanceled(cancellationSignal);
            if (table.equals(MmsDatabase.TABLE_NAME)) {
                count = exportTable(table, input, outputStream, cursor -> isNonExpiringMmsMessage(cursor) && isNotReleaseChannel(cursor), null, count, estimatedCount, cancellationSignal);
            } else if (table.equals(SmsDatabase.TABLE_NAME)) {
                count = exportTable(table, input, outputStream, cursor -> isNonExpiringSmsMessage(cursor) && isNotReleaseChannel(cursor), null, count, estimatedCount, cancellationSignal);
            } else if (table.equals(ReactionDatabase.TABLE_NAME)) {
                count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMessage(input, new MessageId(CursorUtil.requireLong(cursor, ReactionDatabase.MESSAGE_ID), CursorUtil.requireBoolean(cursor, ReactionDatabase.IS_MMS))), null, count, estimatedCount, cancellationSignal);
            } else if (table.equals(MentionDatabase.TABLE_NAME)) {
                count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMmsMessageAndNotReleaseChannel(input, CursorUtil.requireLong(cursor, MentionDatabase.MESSAGE_ID)), null, count, estimatedCount, cancellationSignal);
            } else if (table.equals(GroupReceiptDatabase.TABLE_NAME)) {
                count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMmsMessageAndNotReleaseChannel(input, cursor.getLong(cursor.getColumnIndexOrThrow(GroupReceiptDatabase.MMS_ID))), null, count, estimatedCount, cancellationSignal);
            } else if (table.equals(AttachmentDatabase.TABLE_NAME)) {
                count = exportTable(table, input, outputStream, cursor -> isForNonExpiringMmsMessageAndNotReleaseChannel(input, cursor.getLong(cursor.getColumnIndexOrThrow(AttachmentDatabase.MMS_ID))), (cursor, innerCount) -> exportAttachment(attachmentSecret, cursor, outputStream, innerCount, estimatedCount), count, estimatedCount, cancellationSignal);
            } else if (table.equals(StickerDatabase.TABLE_NAME)) {
                count = exportTable(table, input, outputStream, cursor -> true, (cursor, innerCount) -> exportSticker(attachmentSecret, cursor, outputStream, innerCount, estimatedCount), count, estimatedCount, cancellationSignal);
            } else if (!BLACKLISTED_TABLES.contains(table) && !table.startsWith("sqlite_")) {
                count = exportTable(table, input, outputStream, null, null, count, estimatedCount, cancellationSignal);
            }
            stopwatch.split("table::" + table);
        }
        for (BackupProtos.SharedPreference preference : TextSecurePreferences.getPreferencesToSaveToBackup(context)) {
            throwIfCanceled(cancellationSignal);
            EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
            outputStream.write(preference);
        }
        stopwatch.split("prefs");
        count = exportKeyValues(outputStream, SignalStore.getKeysToIncludeInBackup(), count, estimatedCount, cancellationSignal);
        stopwatch.split("key_values");
        for (AvatarHelper.Avatar avatar : AvatarHelper.getAvatars(context)) {
            throwIfCanceled(cancellationSignal);
            if (avatar != null) {
                EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count, estimatedCount));
                try (InputStream inputStream = avatar.getInputStream()) {
                    outputStream.write(avatar.getFilename(), inputStream, avatar.getLength());
                }
            }
        }
        stopwatch.split("avatars");
        stopwatch.stop(TAG);
        outputStream.writeEnd();
    } finally {
        if (closeOutputStream) {
            outputStream.close();
        }
        EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, ++count, estimatedCountOutside));
    }
}
Also used : RequiresApi(androidx.annotation.RequiresApi) SignalStore(org.thoughtcrime.securesms.keyvalue.SignalStore) NonNull(androidx.annotation.NonNull) SecretKeySpec(javax.crypto.spec.SecretKeySpec) KeyValueDataSet(org.thoughtcrime.securesms.keyvalue.KeyValueDataSet) ModernDecryptingPartInputStream(org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream) SmsDatabase(org.thoughtcrime.securesms.database.SmsDatabase) CursorUtil(org.signal.core.util.CursorUtil) RecipientId(org.thoughtcrime.securesms.recipients.RecipientId) Conversions(org.signal.core.util.Conversions) ReactionDatabase(org.thoughtcrime.securesms.database.ReactionDatabase) SQLiteDatabase(net.zetetic.database.sqlcipher.SQLiteDatabase) HKDFv3(org.signal.libsignal.protocol.kdf.HKDFv3) SearchDatabase(org.thoughtcrime.securesms.database.SearchDatabase) IllegalBlockSizeException(javax.crypto.IllegalBlockSizeException) ApplicationDependencies(org.thoughtcrime.securesms.dependencies.ApplicationDependencies) Set(java.util.Set) Mac(javax.crypto.Mac) AvatarPickerDatabase(org.thoughtcrime.securesms.database.model.AvatarPickerDatabase) SessionDatabase(org.thoughtcrime.securesms.database.SessionDatabase) ByteString(com.google.protobuf.ByteString) Objects(java.util.Objects) Log(org.signal.core.util.logging.Log) ClassicDecryptingPartInputStream(org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream) List(java.util.List) Nullable(androidx.annotation.Nullable) MmsSmsColumns(org.thoughtcrime.securesms.database.MmsSmsColumns) DocumentFile(androidx.documentfile.provider.DocumentFile) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) InvalidKeyException(java.security.InvalidKeyException) SetUtil(org.signal.core.util.SetUtil) MmsDatabase(org.thoughtcrime.securesms.database.MmsDatabase) KeyValueDatabase(org.thoughtcrime.securesms.database.KeyValueDatabase) Context(android.content.Context) StickerDatabase(org.thoughtcrime.securesms.database.StickerDatabase) AttachmentSecret(org.thoughtcrime.securesms.crypto.AttachmentSecret) PendingRetryReceiptDatabase(org.thoughtcrime.securesms.database.PendingRetryReceiptDatabase) EmojiSearchDatabase(org.thoughtcrime.securesms.database.EmojiSearchDatabase) Util(org.thoughtcrime.securesms.util.Util) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase) Cipher(javax.crypto.Cipher) InvalidAlgorithmParameterException(java.security.InvalidAlgorithmParameterException) SignedPreKeyDatabase(org.thoughtcrime.securesms.database.SignedPreKeyDatabase) TextSecurePreferences(org.thoughtcrime.securesms.util.TextSecurePreferences) MentionDatabase(org.thoughtcrime.securesms.database.MentionDatabase) SenderKeySharedDatabase(org.thoughtcrime.securesms.database.SenderKeySharedDatabase) IvParameterSpec(javax.crypto.spec.IvParameterSpec) SenderKeyDatabase(org.thoughtcrime.securesms.database.SenderKeyDatabase) NoSuchPaddingException(javax.crypto.NoSuchPaddingException) EventBus(org.greenrobot.eventbus.EventBus) LinkedList(java.util.LinkedList) Cursor(android.database.Cursor) OutputStream(java.io.OutputStream) OneTimePreKeyDatabase(org.thoughtcrime.securesms.database.OneTimePreKeyDatabase) MessageId(org.thoughtcrime.securesms.database.model.MessageId) GroupReceiptDatabase(org.thoughtcrime.securesms.database.GroupReceiptDatabase) AvatarHelper(org.thoughtcrime.securesms.profiles.AvatarHelper) FileOutputStream(java.io.FileOutputStream) TextUtils(android.text.TextUtils) ByteUtil(org.signal.libsignal.protocol.util.ByteUtil) IOException(java.io.IOException) File(java.io.File) BadPaddingException(javax.crypto.BadPaddingException) AttachmentId(org.thoughtcrime.securesms.attachments.AttachmentId) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) Predicate(com.annimon.stream.function.Predicate) InputStream(java.io.InputStream) ModernDecryptingPartInputStream(org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream) ClassicDecryptingPartInputStream(org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream) InputStream(java.io.InputStream) Stopwatch(org.thoughtcrime.securesms.util.Stopwatch) ByteString(com.google.protobuf.ByteString) AvatarHelper(org.thoughtcrime.securesms.profiles.AvatarHelper) MessageId(org.thoughtcrime.securesms.database.model.MessageId)

Aggregations

AttachmentSecret (org.thoughtcrime.securesms.crypto.AttachmentSecret)22 File (java.io.File)19 NonNull (androidx.annotation.NonNull)15 IOException (java.io.IOException)13 OutputStream (java.io.OutputStream)10 Cursor (android.database.Cursor)7 InputStream (java.io.InputStream)7 AttachmentDatabase (org.thoughtcrime.securesms.database.AttachmentDatabase)7 ClassicDecryptingPartInputStream (org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream)6 Context (android.content.Context)4 TextUtils (android.text.TextUtils)4 Predicate (com.annimon.stream.function.Predicate)4 ByteString (com.google.protobuf.ByteString)4 FileInputStream (java.io.FileInputStream)4 FileOutputStream (java.io.FileOutputStream)4 InvalidAlgorithmParameterException (java.security.InvalidAlgorithmParameterException)4 InvalidKeyException (java.security.InvalidKeyException)4 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)4 LinkedList (java.util.LinkedList)4 List (java.util.List)4