From 929942de9d6a9b95443f14e82639454232e299ea Mon Sep 17 00:00:00 2001 From: Cody Henthorne Date: Thu, 5 Sep 2024 15:49:05 -0400 Subject: [PATCH] Verify digest for backupv2 local media restore. --- .../securesms/backup/v2/local/LocalArchiver.kt | 6 ++---- .../thoughtcrime/securesms/database/AttachmentTable.kt | 8 +++++--- .../securesms/jobs/RestoreLocalAttachmentJob.kt | 8 +++++--- .../java/org/signal/spinner/DefaultColumnTransformer.kt | 4 ++-- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/local/LocalArchiver.kt b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/local/LocalArchiver.kt index f697e715b9..7c73cd0ae3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/backup/v2/local/LocalArchiver.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/backup/v2/local/LocalArchiver.kt @@ -23,7 +23,6 @@ import java.io.IOException import java.io.InputStream import java.io.OutputStream import java.util.Collections -import kotlin.random.Random typealias ArchiveResult = org.signal.core.util.Result @@ -70,10 +69,9 @@ object LocalArchiver { } source()?.use { sourceStream -> - val iv = Random.nextBytes(16) // todo [local-backup] but really do an iv from table + val iv = attachment.remoteIv val combinedKey = Base64.decode(attachment.remoteKey) - - var destination: OutputStream? = filesFileSystem.fileOutputStream(mediaName) + val destination: OutputStream? = filesFileSystem.fileOutputStream(mediaName) if (destination == null) { Log.w(TAG, "Unable to create output file for attachment") diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt index 5ffc515cfd..716c9f8998 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/AttachmentTable.kt @@ -478,7 +478,7 @@ class AttachmentTable( return readableDatabase .select(*PROJECTION) .from(TABLE_NAME) - .where("$REMOTE_KEY IS NOT NULL AND $REMOTE_DIGEST IS NOT NULL AND $DATA_FILE IS NOT NULL") + .where("$REMOTE_KEY IS NOT NULL AND $REMOTE_DIGEST IS NOT NULL AND $REMOTE_IV IS NOT NULL AND $DATA_FILE IS NOT NULL") .orderBy("$ID DESC") .run() .readToList { @@ -487,7 +487,8 @@ class AttachmentTable( random = it.requireNonNullBlob(DATA_RANDOM), size = it.requireLong(DATA_SIZE), remoteDigest = it.requireBlob(REMOTE_DIGEST)!!, - remoteKey = it.requireBlob(REMOTE_KEY)!! + remoteKey = it.requireBlob(REMOTE_KEY)!!, + remoteIv = it.requireBlob(REMOTE_IV)!! ) } } @@ -2550,7 +2551,8 @@ class AttachmentTable( val random: ByteArray, val size: Long, val remoteDigest: ByteArray, - val remoteKey: ByteArray + val remoteKey: ByteArray, + val remoteIv: ByteArray ) class LocalRestorableAttachment( diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreLocalAttachmentJob.kt b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreLocalAttachmentJob.kt index fcb6466b98..26e819f790 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreLocalAttachmentJob.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/RestoreLocalAttachmentJob.kt @@ -6,6 +6,7 @@ package org.thoughtcrime.securesms.jobs import android.net.Uri import org.signal.core.util.Base64 +import org.signal.core.util.StreamUtil import org.signal.core.util.androidx.DocumentFileInfo import org.signal.core.util.logging.Log import org.signal.core.util.withinTransaction @@ -145,9 +146,10 @@ class RestoreLocalAttachmentJob private constructor( val streamSupplier = StreamSupplier { ArchiveFileSystem.openInputStream(context, restoreUri) ?: throw IOException("Unable to open stream") } try { - // TODO [local-backup] actually verify mac and save iv - AttachmentCipherInputStream.createForAttachment(streamSupplier, size, attachment.size, combinedKey, null, null, 0, true).use { input -> - SignalDatabase.attachments.finalizeAttachmentAfterDownload(attachment.mmsId, attachment.attachmentId, input, null) + val iv = ByteArray(16) + streamSupplier.openStream().use { StreamUtil.readFully(it, iv) } + AttachmentCipherInputStream.createForAttachment(streamSupplier, size, attachment.size, combinedKey, attachment.remoteDigest, null, 0, false).use { input -> + SignalDatabase.attachments.finalizeAttachmentAfterDownload(attachment.mmsId, attachment.attachmentId, input, iv) } } catch (e: InvalidMessageException) { Log.w(TAG, "Experienced an InvalidMessageException while trying to read attachment.", e) diff --git a/spinner/lib/src/main/java/org/signal/spinner/DefaultColumnTransformer.kt b/spinner/lib/src/main/java/org/signal/spinner/DefaultColumnTransformer.kt index 53267b0dac..8a79dbe624 100644 --- a/spinner/lib/src/main/java/org/signal/spinner/DefaultColumnTransformer.kt +++ b/spinner/lib/src/main/java/org/signal/spinner/DefaultColumnTransformer.kt @@ -1,7 +1,7 @@ package org.signal.spinner import android.database.Cursor -import android.util.Base64 +import org.signal.core.util.Base64 object DefaultColumnTransformer : ColumnTransformer { override fun matches(tableName: String?, columnName: String): Boolean { @@ -11,7 +11,7 @@ object DefaultColumnTransformer : ColumnTransformer { override fun transform(tableName: String?, columnName: String, cursor: Cursor): String? { val index = cursor.getColumnIndex(columnName) return when (cursor.getType(index)) { - Cursor.FIELD_TYPE_BLOB -> Base64.encodeToString(cursor.getBlob(index), 0) + Cursor.FIELD_TYPE_BLOB -> Base64.encodeWithPadding(cursor.getBlob(index)) else -> cursor.getString(index) } }