Search in sources :

Example 1 with AttachmentSecret

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

the class FullBackupExporter method export.

public static void export(@NonNull Context context, @NonNull AttachmentSecret attachmentSecret, @NonNull SQLiteDatabase input, @NonNull File output, @NonNull String passphrase) throws IOException {
    BackupFrameOutputStream outputStream = new BackupFrameOutputStream(output, passphrase);
    outputStream.writeDatabaseVersion(input.getVersion());
    List<String> tables = exportSchema(input, outputStream);
    int count = 0;
    for (String table : tables) {
        if (table.equals(SmsDatabase.TABLE_NAME) || table.equals(MmsDatabase.TABLE_NAME)) {
            count = exportTable(table, input, outputStream, cursor -> cursor.getInt(cursor.getColumnIndexOrThrow(MmsSmsColumns.EXPIRES_IN)) <= 0, null, count);
        } else if (table.equals(AttachmentDatabase.TABLE_NAME)) {
            count = exportTable(table, input, outputStream, null, cursor -> exportAttachment(attachmentSecret, cursor, outputStream), count);
        } else if (!table.equals(SignedPreKeyDatabase.TABLE_NAME) && !table.equals(OneTimePreKeyDatabase.TABLE_NAME) && !table.equals(SessionDatabase.TABLE_NAME)) {
            count = exportTable(table, input, outputStream, null, null, count);
        }
    }
    for (BackupProtos.SharedPreference preference : IdentityKeyUtil.getBackupRecord(context)) {
        EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
        outputStream.write(preference);
    }
    for (File avatar : AvatarHelper.getAvatarFiles(context)) {
        EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.PROGRESS, ++count));
        outputStream.write(avatar.getName(), new FileInputStream(avatar), avatar.length());
    }
    outputStream.writeEnd();
    outputStream.close();
    EventBus.getDefault().post(new BackupEvent(BackupEvent.Type.FINISHED, ++count));
}
Also used : Context(android.content.Context) AttachmentSecret(org.thoughtcrime.securesms.crypto.AttachmentSecret) HKDFv3(org.whispersystems.libsignal.kdf.HKDFv3) Util(org.thoughtcrime.securesms.util.Util) SecretKeySpec(javax.crypto.spec.SecretKeySpec) Address(org.thoughtcrime.securesms.database.Address) NonNull(android.support.annotation.NonNull) AttachmentDatabase(org.thoughtcrime.securesms.database.AttachmentDatabase) Cipher(javax.crypto.Cipher) ModernDecryptingPartInputStream(org.thoughtcrime.securesms.crypto.ModernDecryptingPartInputStream) InvalidAlgorithmParameterException(java.security.InvalidAlgorithmParameterException) SignedPreKeyDatabase(org.thoughtcrime.securesms.database.SignedPreKeyDatabase) SmsDatabase(org.thoughtcrime.securesms.database.SmsDatabase) IvParameterSpec(javax.crypto.spec.IvParameterSpec) NoSuchPaddingException(javax.crypto.NoSuchPaddingException) Consumer(com.annimon.stream.function.Consumer) EventBus(org.greenrobot.eventbus.EventBus) LinkedList(java.util.LinkedList) Log(android.util.Log) IdentityKeyUtil(org.thoughtcrime.securesms.crypto.IdentityKeyUtil) Cursor(android.database.Cursor) OutputStream(java.io.OutputStream) OneTimePreKeyDatabase(org.thoughtcrime.securesms.database.OneTimePreKeyDatabase) Conversions(org.thoughtcrime.securesms.util.Conversions) AvatarHelper(org.thoughtcrime.securesms.profiles.AvatarHelper) IllegalBlockSizeException(javax.crypto.IllegalBlockSizeException) FileOutputStream(java.io.FileOutputStream) Mac(javax.crypto.Mac) TextUtils(android.text.TextUtils) IOException(java.io.IOException) FileInputStream(java.io.FileInputStream) ByteUtil(org.whispersystems.libsignal.util.ByteUtil) File(java.io.File) SessionDatabase(org.thoughtcrime.securesms.database.SessionDatabase) ByteString(com.google.protobuf.ByteString) ClassicDecryptingPartInputStream(org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream) List(java.util.List) BadPaddingException(javax.crypto.BadPaddingException) MmsSmsColumns(org.thoughtcrime.securesms.database.MmsSmsColumns) SQLiteDatabase(net.sqlcipher.database.SQLiteDatabase) AttachmentId(org.thoughtcrime.securesms.attachments.AttachmentId) NoSuchAlgorithmException(java.security.NoSuchAlgorithmException) Predicate(com.annimon.stream.function.Predicate) InvalidKeyException(java.security.InvalidKeyException) Nullable(android.support.annotation.Nullable) MmsDatabase(org.thoughtcrime.securesms.database.MmsDatabase) InputStream(java.io.InputStream) ByteString(com.google.protobuf.ByteString) File(java.io.File) FileInputStream(java.io.FileInputStream)

Example 2 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 3 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.whispersystems.libsignal.InvalidMessageException) IdentityKey(org.whispersystems.libsignal.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 4 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.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.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) 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 5 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, DatabaseUpgradeActivity.DatabaseUpgradeListener listener) {
    SQLiteDatabase db = getWritableDatabase();
    db.beginTransaction();
    if (fromVersion < DatabaseUpgradeActivity.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.w("DatabaseFactory", "Upgrade count: " + (smsCount + threadCount));
        do {
            Log.w("DatabaseFactory", "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("DatabaseFactory", e);
                }
            }
            skip += ROW_LIMIT;
        } while (smsCursor != null && smsCursor.getCount() > 0);
        Cursor threadCursor = null;
        skip = 0;
        do {
            Log.w("DatabaseFactory", "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("DatabaseFactory", e);
                }
            }
            skip += ROW_LIMIT;
        } while (threadCursor != null && threadCursor.getCount() > 0);
        if (smsCursor != null)
            smsCursor.close();
        if (threadCursor != null)
            threadCursor.close();
    }
    if (fromVersion < DatabaseUpgradeActivity.MMS_BODY_VERSION) {
        Log.w("DatabaseFactory", "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.w("DatabaseFactory", "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) ? Util.readFullyAsString(is) : body + " " + Util.readFullyAsString(is);
                        // noinspection ResultOfMethodCallIgnored
                        dataFile.delete();
                        db.delete("part", "_id = ?", new String[] { partId + "" });
                    } catch (IOException e) {
                        Log.w("DatabaseFactory", 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.w("DatabaseFactory", "Updated body: " + body + " and part_count: " + partCount);
        }
    }
    if (fromVersion < DatabaseUpgradeActivity.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 < DatabaseUpgradeActivity.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);
    MessageNotifier.updateNotification(context);
}
Also used : ContentValues(android.content.ContentValues) InvalidMessageException(org.whispersystems.libsignal.InvalidMessageException) IdentityKey(org.whispersystems.libsignal.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)

Aggregations

AttachmentSecret (org.thoughtcrime.securesms.crypto.AttachmentSecret)9 File (java.io.File)8 IOException (java.io.IOException)6 NonNull (androidx.annotation.NonNull)5 Cursor (android.database.Cursor)4 InputStream (java.io.InputStream)4 OutputStream (java.io.OutputStream)4 ClassicDecryptingPartInputStream (org.thoughtcrime.securesms.crypto.ClassicDecryptingPartInputStream)4 FileInputStream (java.io.FileInputStream)3 ContentValues (android.content.ContentValues)2 Context (android.content.Context)2 SQLiteDatabase (android.database.sqlite.SQLiteDatabase)2 TextUtils (android.text.TextUtils)2 Predicate (com.annimon.stream.function.Predicate)2 ByteString (com.google.protobuf.ByteString)2 FileOutputStream (java.io.FileOutputStream)2 InvalidAlgorithmParameterException (java.security.InvalidAlgorithmParameterException)2 InvalidKeyException (java.security.InvalidKeyException)2 NoSuchAlgorithmException (java.security.NoSuchAlgorithmException)2 LinkedList (java.util.LinkedList)2