Add padding to the gzipped backup output.

This commit is contained in:
Greyson Parrelli
2024-04-19 12:09:11 -04:00
committed by Cody Henthorne
parent da43ff1e95
commit f34ae8d118
3 changed files with 86 additions and 9 deletions

View File

@@ -13,7 +13,6 @@ import org.whispersystems.signalservice.api.backup.BackupKey
import org.whispersystems.signalservice.api.push.ServiceId.ACI
import java.io.IOException
import java.io.OutputStream
import java.util.zip.GZIPOutputStream
import javax.crypto.Cipher
import javax.crypto.CipherOutputStream
import javax.crypto.Mac
@@ -33,7 +32,7 @@ class EncryptedBackupWriter(
private val append: (ByteArray) -> Unit
) : BackupExportWriter {
private val mainStream: GZIPOutputStream
private val mainStream: PaddedGzipOutputStream
private val macStream: MacOutputStream
init {
@@ -48,13 +47,9 @@ class EncryptedBackupWriter(
}
macStream = MacOutputStream(outputStream, mac)
val cipherStream = CipherOutputStream(macStream, cipher)
mainStream = GZIPOutputStream(
CipherOutputStream(
macStream,
cipher
)
)
mainStream = PaddedGzipOutputStream(cipherStream)
}
override fun write(header: BackupInfo) {

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2024 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.backup.v2.stream
import org.whispersystems.signalservice.internal.crypto.PaddingInputStream
import java.io.FilterOutputStream
import java.io.OutputStream
import java.util.zip.GZIPOutputStream
/**
* GZIPs the content of the provided [outputStream], but also adds padding to the end of the stream using the same algorithm as [PaddingInputStream].
* We do this to fit files into a smaller number of size buckets to avoid fingerprinting. And it turns out that bolting on zeros to the end of a GZIP stream is
* fine, because GZIP is smart enough to ignore it. This means readers of this data don't have to do anything special.
*/
class PaddedGzipOutputStream private constructor(private val outputStream: SizeObservingOutputStream) : GZIPOutputStream(outputStream) {
constructor(outputStream: OutputStream) : this(SizeObservingOutputStream(outputStream))
override fun finish() {
super.finish()
val totalLength = outputStream.size
val paddedSize: Long = PaddingInputStream.getPaddedSize(totalLength)
val paddingToAdd: Int = (paddedSize - totalLength).toInt()
outputStream.write(ByteArray(paddingToAdd))
}
/**
* We need to know the size of the *compressed* stream to know how much padding to add at the end.
*/
private class SizeObservingOutputStream(val wrapped: OutputStream) : FilterOutputStream(wrapped) {
var size: Long = 0L
private set
override fun write(b: Int) {
wrapped.write(b)
size++
}
override fun write(b: ByteArray) {
wrapped.write(b)
size += b.size
}
override fun write(b: ByteArray, off: Int, len: Int) {
wrapped.write(b, off, len)
size += len
}
}
}