Do K/V backup with new version 1 with new crypto

Restoring still supports version 0 with old crypto
This commit is contained in:
Torsten Grote 2021-09-08 16:01:01 +02:00 committed by Chirayu Desai
parent 3ffb79b04f
commit 0c3ea7679b
7 changed files with 105 additions and 119 deletions

View file

@ -1,6 +1,8 @@
package com.stevesoltys.seedvault.header package com.stevesoltys.seedvault.header
import com.stevesoltys.seedvault.crypto.GCM_AUTHENTICATION_TAG_LENGTH import com.stevesoltys.seedvault.crypto.GCM_AUTHENTICATION_TAG_LENGTH
import com.stevesoltys.seedvault.crypto.TYPE_BACKUP_KV
import java.nio.ByteBuffer
internal const val VERSION: Byte = 1 internal const val VERSION: Byte = 1
internal const val MAX_PACKAGE_LENGTH_SIZE = 255 internal const val MAX_PACKAGE_LENGTH_SIZE = 255
@ -29,6 +31,15 @@ data class VersionHeader(
} }
} }
internal fun getADForKV(version: Byte, packageName: String): ByteArray {
val packageNameBytes = packageName.toByteArray()
return ByteBuffer.allocate(2 + packageNameBytes.size)
.put(version)
.put(TYPE_BACKUP_KV)
.put(packageNameBytes)
.array()
}
internal const val SEGMENT_LENGTH_SIZE: Int = Short.SIZE_BYTES internal const val SEGMENT_LENGTH_SIZE: Int = Short.SIZE_BYTES
internal const val MAX_SEGMENT_LENGTH: Int = Short.MAX_VALUE.toInt() internal const val MAX_SEGMENT_LENGTH: Int = Short.MAX_VALUE.toInt()
internal const val MAX_SEGMENT_CLEARTEXT_LENGTH: Int = internal const val MAX_SEGMENT_CLEARTEXT_LENGTH: Int =

View file

@ -13,10 +13,11 @@ import com.stevesoltys.seedvault.MAGIC_PACKAGE_MANAGER
import com.stevesoltys.seedvault.crypto.Crypto import com.stevesoltys.seedvault.crypto.Crypto
import com.stevesoltys.seedvault.encodeBase64 import com.stevesoltys.seedvault.encodeBase64
import com.stevesoltys.seedvault.header.HeaderWriter import com.stevesoltys.seedvault.header.HeaderWriter
import com.stevesoltys.seedvault.header.VERSION
import com.stevesoltys.seedvault.header.VersionHeader import com.stevesoltys.seedvault.header.VersionHeader
import com.stevesoltys.seedvault.header.getADForKV
import com.stevesoltys.seedvault.settings.SettingsManager import com.stevesoltys.seedvault.settings.SettingsManager
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import libcore.io.IoUtils.closeQuietly
import java.io.IOException import java.io.IOException
class KVBackupState(internal val packageInfo: PackageInfo) class KVBackupState(internal val packageInfo: PackageInfo)
@ -166,18 +167,17 @@ internal class KVBackup(
Log.e(TAG, "Deleting record with base64Key ${op.base64Key}") Log.e(TAG, "Deleting record with base64Key ${op.base64Key}")
plugin.deleteRecord(packageInfo, op.base64Key) plugin.deleteRecord(packageInfo, op.base64Key)
} else { } else {
val outputStream = plugin.getOutputStreamForRecord(packageInfo, op.base64Key) plugin.getOutputStreamForRecord(packageInfo, op.base64Key).use { outputStream ->
try {
val header = VersionHeader( val header = VersionHeader(
packageName = packageInfo.packageName, packageName = packageInfo.packageName,
key = op.key key = op.key
) )
headerWriter.writeVersion(outputStream, header) headerWriter.writeVersion(outputStream, header)
crypto.encryptHeader(outputStream, header) val ad = getADForKV(VERSION, packageInfo.packageName)
crypto.encryptMultipleSegments(outputStream, op.value) crypto.newEncryptingStream(outputStream, ad).use { encryptedStream ->
outputStream.flush() encryptedStream.write(op.value)
} finally { encryptedStream.flush()
closeQuietly(outputStream) }
} }
} }
} }

View file

@ -13,6 +13,8 @@ import com.stevesoltys.seedvault.crypto.Crypto
import com.stevesoltys.seedvault.decodeBase64 import com.stevesoltys.seedvault.decodeBase64
import com.stevesoltys.seedvault.header.HeaderReader import com.stevesoltys.seedvault.header.HeaderReader
import com.stevesoltys.seedvault.header.UnsupportedVersionException import com.stevesoltys.seedvault.header.UnsupportedVersionException
import com.stevesoltys.seedvault.header.VERSION
import com.stevesoltys.seedvault.header.getADForKV
import libcore.io.IoUtils.closeQuietly import libcore.io.IoUtils.closeQuietly
import java.io.IOException import java.io.IOException
import java.util.ArrayList import java.util.ArrayList
@ -146,8 +148,16 @@ internal class KVRestore(
) = plugin.getInputStreamForRecord(state.token, state.packageInfo, dKey.base64Key) ) = plugin.getInputStreamForRecord(state.token, state.packageInfo, dKey.base64Key)
.use { inputStream -> .use { inputStream ->
val version = headerReader.readVersion(inputStream) val version = headerReader.readVersion(inputStream)
crypto.decryptHeader(inputStream, version, state.packageInfo.packageName, dKey.key) val packageName = state.packageInfo.packageName
val value = crypto.decryptMultipleSegments(inputStream) val value = if (version == 0.toByte()) {
crypto.decryptHeader(inputStream, version, packageName, dKey.key)
crypto.decryptMultipleSegments(inputStream)
} else {
val ad = getADForKV(VERSION, packageName)
crypto.newDecryptingStream(inputStream, ad).use { decryptedStream ->
decryptedStream.readBytes()
}
}
val size = value.size val size = value.size
Log.v(TAG, " ... key=${dKey.key} size=$size") Log.v(TAG, " ... key=${dKey.key} size=$size")

View file

@ -13,6 +13,7 @@ internal abstract class BackupTest : TransportTest() {
protected val headerWriter = mockk<HeaderWriter>() protected val headerWriter = mockk<HeaderWriter>()
protected val data = mockk<ParcelFileDescriptor>() protected val data = mockk<ParcelFileDescriptor>()
protected val outputStream = mockk<OutputStream>() protected val outputStream = mockk<OutputStream>()
protected val encryptedOutputStream = mockk<OutputStream>()
protected val header = VersionHeader(packageName = packageInfo.packageName) protected val header = VersionHeader(packageName = packageInfo.packageName)
protected val quota = 42L protected val quota = 42L

View file

@ -12,7 +12,9 @@ import com.stevesoltys.seedvault.Utf8
import com.stevesoltys.seedvault.getRandomString import com.stevesoltys.seedvault.getRandomString
import com.stevesoltys.seedvault.header.MAX_KEY_LENGTH_SIZE import com.stevesoltys.seedvault.header.MAX_KEY_LENGTH_SIZE
import com.stevesoltys.seedvault.header.VersionHeader import com.stevesoltys.seedvault.header.VersionHeader
import com.stevesoltys.seedvault.header.getADForKV
import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager import com.stevesoltys.seedvault.ui.notification.BackupNotificationManager
import io.mockk.CapturingSlot
import io.mockk.Runs import io.mockk.Runs
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.every import io.mockk.every
@ -47,7 +49,7 @@ internal class KVBackupTest : BackupTest() {
private val key = getRandomString(MAX_KEY_LENGTH_SIZE) private val key = getRandomString(MAX_KEY_LENGTH_SIZE)
private val key64 = Base64.getEncoder().encodeToString(key.toByteArray(Utf8)) private val key64 = Base64.getEncoder().encodeToString(key.toByteArray(Utf8))
private val value = ByteArray(23).apply { Random.nextBytes(this) } private val dataValue = Random.nextBytes(23)
private val versionHeader = VersionHeader(packageName = packageInfo.packageName, key = key) private val versionHeader = VersionHeader(packageName = packageInfo.packageName, key = key)
@Test @Test
@ -73,26 +75,26 @@ internal class KVBackupTest : BackupTest() {
every { dataInput.readNextHeader() } returnsMany listOf(true, true, false) every { dataInput.readNextHeader() } returnsMany listOf(true, true, false)
every { dataInput.key } returnsMany listOf("key1", "key2") every { dataInput.key } returnsMany listOf("key1", "key2")
// we don't care about values, so just use the same one always // we don't care about values, so just use the same one always
every { dataInput.dataSize } returns value.size every { dataInput.dataSize } returns dataValue.size
every { dataInput.readEntityData(any(), 0, value.size) } returns value.size every { dataInput.readEntityData(any(), 0, dataValue.size) } returns dataValue.size
// store first record and show notification for it // store first record and show notification for it
every { notificationManager.onPmKvBackup("key1", 1, 2) } just Runs every { notificationManager.onPmKvBackup("key1", 1, 2) } just Runs
coEvery { plugin.getOutputStreamForRecord(pmPackageInfo, "a2V5MQ") } returns outputStream coEvery { plugin.getOutputStreamForRecord(pmPackageInfo, "a2V5MQ") } returns outputStream
val versionHeader1 = VersionHeader(packageName = pmPackageInfo.packageName, key = "key1") val versionHeader1 = VersionHeader(packageName = pmPackageInfo.packageName, key = "key1")
every { headerWriter.writeVersion(outputStream, versionHeader1) } just Runs every { headerWriter.writeVersion(outputStream, versionHeader1) } just Runs
every { crypto.encryptHeader(outputStream, versionHeader1) } just Runs
// store second record and show notification for it // store second record and show notification for it
every { notificationManager.onPmKvBackup("key2", 2, 2) } just Runs every { notificationManager.onPmKvBackup("key2", 2, 2) } just Runs
coEvery { plugin.getOutputStreamForRecord(pmPackageInfo, "a2V5Mg") } returns outputStream coEvery { plugin.getOutputStreamForRecord(pmPackageInfo, "a2V5Mg") } returns outputStream
val versionHeader2 = VersionHeader(packageName = pmPackageInfo.packageName, key = "key2") val versionHeader2 = VersionHeader(packageName = pmPackageInfo.packageName, key = "key2")
every { headerWriter.writeVersion(outputStream, versionHeader2) } just Runs every { headerWriter.writeVersion(outputStream, versionHeader2) } just Runs
every { crypto.encryptHeader(outputStream, versionHeader2) } just Runs
// encrypt to and close output stream // encrypt to and close output stream
every { crypto.encryptMultipleSegments(outputStream, any()) } just Runs every { crypto.newEncryptingStream(outputStream, any()) } returns encryptedOutputStream
every { outputStream.write(value) } just Runs every { encryptedOutputStream.write(any<ByteArray>()) } just Runs
every { encryptedOutputStream.flush() } just Runs
every { encryptedOutputStream.close() } just Runs
every { outputStream.flush() } just Runs every { outputStream.flush() } just Runs
every { outputStream.close() } just Runs every { outputStream.close() } just Runs
@ -190,8 +192,8 @@ internal class KVBackupTest : BackupTest() {
createBackupDataInput() createBackupDataInput()
every { dataInput.readNextHeader() } returns true every { dataInput.readNextHeader() } returns true
every { dataInput.key } returns key every { dataInput.key } returns key
every { dataInput.dataSize } returns value.size every { dataInput.dataSize } returns dataValue.size
every { dataInput.readEntityData(any(), 0, value.size) } throws IOException() every { dataInput.readEntityData(any(), 0, dataValue.size) } throws IOException()
every { plugin.packageFinished(packageInfo) } just Runs every { plugin.packageFinished(packageInfo) } just Runs
assertEquals(TRANSPORT_ERROR, backup.performBackup(packageInfo, data, 0)) assertEquals(TRANSPORT_ERROR, backup.performBackup(packageInfo, data, 0))
@ -230,10 +232,7 @@ internal class KVBackupTest : BackupTest() {
initPlugin(false) initPlugin(false)
getDataInput(listOf(true)) getDataInput(listOf(true))
writeHeaderAndEncrypt() writeHeaderAndEncrypt()
coEvery { plugin.getOutputStreamForRecord(packageInfo, key64) } returns outputStream every { encryptedOutputStream.write(dataValue) } throws IOException()
every { headerWriter.writeVersion(outputStream, versionHeader) } just Runs
every { crypto.encryptMultipleSegments(outputStream, any()) } throws IOException()
every { outputStream.close() } just Runs
every { plugin.packageFinished(packageInfo) } just Runs every { plugin.packageFinished(packageInfo) } just Runs
assertEquals(TRANSPORT_ERROR, backup.performBackup(packageInfo, data, 0)) assertEquals(TRANSPORT_ERROR, backup.performBackup(packageInfo, data, 0))
@ -247,8 +246,9 @@ internal class KVBackupTest : BackupTest() {
initPlugin(false) initPlugin(false)
getDataInput(listOf(true)) getDataInput(listOf(true))
writeHeaderAndEncrypt() writeHeaderAndEncrypt()
every { outputStream.write(value) } just Runs every { encryptedOutputStream.write(dataValue) } just Runs
every { outputStream.flush() } throws IOException() every { encryptedOutputStream.flush() } throws IOException()
every { encryptedOutputStream.close() } just Runs
every { outputStream.close() } just Runs every { outputStream.close() } just Runs
every { plugin.packageFinished(packageInfo) } just Runs every { plugin.packageFinished(packageInfo) } just Runs
@ -263,9 +263,10 @@ internal class KVBackupTest : BackupTest() {
initPlugin(false) initPlugin(false)
getDataInput(listOf(true, false)) getDataInput(listOf(true, false))
writeHeaderAndEncrypt() writeHeaderAndEncrypt()
every { outputStream.write(value) } just Runs every { encryptedOutputStream.write(dataValue) } just Runs
every { outputStream.flush() } just Runs every { encryptedOutputStream.flush() } just Runs
every { outputStream.close() } throws IOException() every { encryptedOutputStream.close() } just Runs
every { outputStream.close() } just Runs
every { plugin.packageFinished(packageInfo) } just Runs every { plugin.packageFinished(packageInfo) } just Runs
assertEquals(TRANSPORT_OK, backup.performBackup(packageInfo, data, 0)) assertEquals(TRANSPORT_OK, backup.performBackup(packageInfo, data, 0))
@ -278,8 +279,9 @@ internal class KVBackupTest : BackupTest() {
initPlugin(hasDataForPackage) initPlugin(hasDataForPackage)
getDataInput(listOf(true, false)) getDataInput(listOf(true, false))
writeHeaderAndEncrypt() writeHeaderAndEncrypt()
every { outputStream.write(value) } just Runs every { encryptedOutputStream.write(dataValue) } just Runs
every { outputStream.flush() } just Runs every { encryptedOutputStream.flush() } just Runs
every { encryptedOutputStream.close() } just Runs
every { outputStream.close() } just Runs every { outputStream.close() } just Runs
every { plugin.packageFinished(packageInfo) } just Runs every { plugin.packageFinished(packageInfo) } just Runs
} }
@ -296,15 +298,19 @@ internal class KVBackupTest : BackupTest() {
createBackupDataInput() createBackupDataInput()
every { dataInput.readNextHeader() } returnsMany returnValues every { dataInput.readNextHeader() } returnsMany returnValues
every { dataInput.key } returns key every { dataInput.key } returns key
every { dataInput.dataSize } returns value.size every { dataInput.dataSize } returns dataValue.size
every { dataInput.readEntityData(any(), 0, value.size) } returns value.size val slot = CapturingSlot<ByteArray>()
every { dataInput.readEntityData(capture(slot), 0, dataValue.size) } answers {
dataValue.copyInto(slot.captured)
dataValue.size
}
} }
private fun writeHeaderAndEncrypt() { private fun writeHeaderAndEncrypt() {
coEvery { plugin.getOutputStreamForRecord(packageInfo, key64) } returns outputStream coEvery { plugin.getOutputStreamForRecord(packageInfo, key64) } returns outputStream
every { headerWriter.writeVersion(outputStream, versionHeader) } just Runs every { headerWriter.writeVersion(outputStream, versionHeader) } just Runs
every { crypto.encryptHeader(outputStream, versionHeader) } just Runs val ad = getADForKV(versionHeader.version, packageInfo.packageName)
every { crypto.encryptMultipleSegments(outputStream, any()) } just Runs every { crypto.newEncryptingStream(outputStream, ad) } returns encryptedOutputStream
} }
} }

View file

@ -9,11 +9,13 @@ import com.stevesoltys.seedvault.getRandomByteArray
import com.stevesoltys.seedvault.header.UnsupportedVersionException import com.stevesoltys.seedvault.header.UnsupportedVersionException
import com.stevesoltys.seedvault.header.VERSION import com.stevesoltys.seedvault.header.VERSION
import com.stevesoltys.seedvault.header.VersionHeader import com.stevesoltys.seedvault.header.VersionHeader
import com.stevesoltys.seedvault.header.getADForKV
import io.mockk.Runs import io.mockk.Runs
import io.mockk.coEvery import io.mockk.coEvery
import io.mockk.every import io.mockk.every
import io.mockk.just import io.mockk.just
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.verifyAll import io.mockk.verifyAll
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
@ -28,13 +30,17 @@ internal class KVRestoreTest : RestoreTest() {
private val plugin = mockk<KVRestorePlugin>() private val plugin = mockk<KVRestorePlugin>()
private val output = mockk<BackupDataOutput>() private val output = mockk<BackupDataOutput>()
private val restore = KVRestore(plugin, outputFactory, headerReader, crypto) private val restore = KVRestore(plugin, outputFactory, headerReader, crypto)
private val ad = getADForKV(VERSION, packageInfo.packageName)
private val key = "Restore Key" private val key = "Restore Key"
private val key64 = key.encodeBase64() private val key64 = key.encodeBase64()
private val versionHeader = VersionHeader(VERSION, packageInfo.packageName, key)
private val key2 = "Restore Key2" private val key2 = "Restore Key2"
private val key264 = key2.encodeBase64() private val key264 = key2.encodeBase64()
private val versionHeader2 = VersionHeader(VERSION, packageInfo.packageName, key2)
init {
// for InputStream#readBytes()
mockkStatic("kotlin.io.ByteStreamsKt")
}
@Test @Test
fun `hasDataForPackage() delegates to plugin`() = runBlocking { fun `hasDataForPackage() delegates to plugin`() = runBlocking {
@ -90,21 +96,13 @@ internal class KVRestoreTest : RestoreTest() {
} }
@Test @Test
fun `decrypting segment throws`() = runBlocking { fun `decrypting stream throws`() = runBlocking {
restore.initializeState(token, packageInfo) restore.initializeState(token, packageInfo)
getRecordsAndOutput() getRecordsAndOutput()
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream
every { headerReader.readVersion(inputStream) } returns VERSION every { headerReader.readVersion(inputStream) } returns VERSION
every { every { crypto.newDecryptingStream(inputStream, ad) } throws IOException()
crypto.decryptHeader(
inputStream,
VERSION,
packageInfo.packageName,
key
)
} returns versionHeader
every { crypto.decryptMultipleSegments(inputStream) } throws IOException()
streamsGetClosed() streamsGetClosed()
assertEquals(TRANSPORT_ERROR, restore.getRestoreData(fileDescriptor)) assertEquals(TRANSPORT_ERROR, restore.getRestoreData(fileDescriptor))
@ -112,41 +110,13 @@ internal class KVRestoreTest : RestoreTest() {
} }
@Test @Test
fun `decrypting header throws`() = runBlocking { fun `decrypting stream throws security exception`() = runBlocking {
restore.initializeState(token, packageInfo) restore.initializeState(token, packageInfo)
getRecordsAndOutput() getRecordsAndOutput()
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream
every { headerReader.readVersion(inputStream) } returns VERSION every { headerReader.readVersion(inputStream) } returns VERSION
every { every { crypto.newDecryptingStream(inputStream, ad) } throws SecurityException()
crypto.decryptHeader(
inputStream,
VERSION,
packageInfo.packageName,
key
)
} throws IOException()
streamsGetClosed()
assertEquals(TRANSPORT_ERROR, restore.getRestoreData(fileDescriptor))
verifyStreamWasClosed()
}
@Test
fun `decrypting header throws security exception`() = runBlocking {
restore.initializeState(token, packageInfo)
getRecordsAndOutput()
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream
every { headerReader.readVersion(inputStream) } returns VERSION
every {
crypto.decryptHeader(
inputStream,
VERSION,
packageInfo.packageName,
key
)
} throws SecurityException()
streamsGetClosed() streamsGetClosed()
assertEquals(TRANSPORT_ERROR, restore.getRestoreData(fileDescriptor)) assertEquals(TRANSPORT_ERROR, restore.getRestoreData(fileDescriptor))
@ -160,15 +130,8 @@ internal class KVRestoreTest : RestoreTest() {
getRecordsAndOutput() getRecordsAndOutput()
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream
every { headerReader.readVersion(inputStream) } returns VERSION every { headerReader.readVersion(inputStream) } returns VERSION
every { every { crypto.newDecryptingStream(inputStream, ad) } returns decryptedInputStream
crypto.decryptHeader( every { decryptedInputStream.readBytes() } returns data
inputStream,
VERSION,
packageInfo.packageName,
key
)
} returns versionHeader
every { crypto.decryptMultipleSegments(inputStream) } returns data
every { output.writeEntityHeader(key, data.size) } throws IOException() every { output.writeEntityHeader(key, data.size) } throws IOException()
streamsGetClosed() streamsGetClosed()
@ -183,15 +146,8 @@ internal class KVRestoreTest : RestoreTest() {
getRecordsAndOutput() getRecordsAndOutput()
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream
every { headerReader.readVersion(inputStream) } returns VERSION every { headerReader.readVersion(inputStream) } returns VERSION
every { every { crypto.newDecryptingStream(inputStream, ad) } returns decryptedInputStream
crypto.decryptHeader( every { decryptedInputStream.readBytes() } returns data
inputStream,
VERSION,
packageInfo.packageName,
key
)
} returns versionHeader
every { crypto.decryptMultipleSegments(inputStream) } returns data
every { output.writeEntityHeader(key, data.size) } returns 42 every { output.writeEntityHeader(key, data.size) } returns 42
every { output.writeEntityData(data, data.size) } throws IOException() every { output.writeEntityData(data, data.size) } throws IOException()
streamsGetClosed() streamsGetClosed()
@ -207,14 +163,26 @@ internal class KVRestoreTest : RestoreTest() {
getRecordsAndOutput() getRecordsAndOutput()
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream
every { headerReader.readVersion(inputStream) } returns VERSION every { headerReader.readVersion(inputStream) } returns VERSION
every { crypto.newDecryptingStream(inputStream, ad) } returns decryptedInputStream
every { decryptedInputStream.readBytes() } returns data
every { output.writeEntityHeader(key, data.size) } returns 42
every { output.writeEntityData(data, data.size) } returns data.size
streamsGetClosed()
assertEquals(TRANSPORT_OK, restore.getRestoreData(fileDescriptor))
verifyStreamWasClosed()
}
@Test
fun `writing value uses old v0 code`() = runBlocking {
restore.initializeState(token, packageInfo)
getRecordsAndOutput()
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream
every { headerReader.readVersion(inputStream) } returns 0.toByte()
every { every {
crypto.decryptHeader( crypto.decryptHeader(inputStream, 0.toByte(), packageInfo.packageName, key)
inputStream, } returns VersionHeader(VERSION, packageInfo.packageName, key)
VERSION,
packageInfo.packageName,
key
)
} returns versionHeader
every { crypto.decryptMultipleSegments(inputStream) } returns data every { crypto.decryptMultipleSegments(inputStream) } returns data
every { output.writeEntityHeader(key, data.size) } returns 42 every { output.writeEntityHeader(key, data.size) } returns 42
every { output.writeEntityData(data, data.size) } returns data.size every { output.writeEntityData(data, data.size) } returns data.size
@ -228,37 +196,25 @@ internal class KVRestoreTest : RestoreTest() {
fun `writing two values succeeds`() = runBlocking { fun `writing two values succeeds`() = runBlocking {
val data2 = getRandomByteArray() val data2 = getRandomByteArray()
val inputStream2 = mockk<InputStream>() val inputStream2 = mockk<InputStream>()
val decryptedInputStream2 = mockk<InputStream>()
restore.initializeState(token, packageInfo) restore.initializeState(token, packageInfo)
getRecordsAndOutput(listOf(key64, key264)) getRecordsAndOutput(listOf(key64, key264))
// first key/value // first key/value
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream coEvery { plugin.getInputStreamForRecord(token, packageInfo, key64) } returns inputStream
every { headerReader.readVersion(inputStream) } returns VERSION every { headerReader.readVersion(inputStream) } returns VERSION
every { every { crypto.newDecryptingStream(inputStream, ad) } returns decryptedInputStream
crypto.decryptHeader( every { decryptedInputStream.readBytes() } returns data
inputStream,
VERSION,
packageInfo.packageName,
key
)
} returns versionHeader
every { crypto.decryptMultipleSegments(inputStream) } returns data
every { output.writeEntityHeader(key, data.size) } returns 42 every { output.writeEntityHeader(key, data.size) } returns 42
every { output.writeEntityData(data, data.size) } returns data.size every { output.writeEntityData(data, data.size) } returns data.size
// second key/value // second key/value
coEvery { plugin.getInputStreamForRecord(token, packageInfo, key264) } returns inputStream2 coEvery { plugin.getInputStreamForRecord(token, packageInfo, key264) } returns inputStream2
every { headerReader.readVersion(inputStream2) } returns VERSION every { headerReader.readVersion(inputStream2) } returns VERSION
every { every { crypto.newDecryptingStream(inputStream2, ad) } returns decryptedInputStream2
crypto.decryptHeader( every { decryptedInputStream2.readBytes() } returns data2
inputStream2,
VERSION,
packageInfo.packageName,
key2
)
} returns versionHeader2
every { crypto.decryptMultipleSegments(inputStream2) } returns data2
every { output.writeEntityHeader(key2, data2.size) } returns 42 every { output.writeEntityHeader(key2, data2.size) } returns 42
every { output.writeEntityData(data2, data2.size) } returns data2.size every { output.writeEntityData(data2, data2.size) } returns data2.size
every { decryptedInputStream2.close() } just Runs
every { inputStream2.close() } just Runs every { inputStream2.close() } just Runs
streamsGetClosed() streamsGetClosed()
@ -271,6 +227,7 @@ internal class KVRestoreTest : RestoreTest() {
} }
private fun streamsGetClosed() { private fun streamsGetClosed() {
every { decryptedInputStream.close() } just Runs
every { inputStream.close() } just Runs every { inputStream.close() } just Runs
every { fileDescriptor.close() } just Runs every { fileDescriptor.close() } just Runs
} }

View file

@ -16,6 +16,7 @@ internal abstract class RestoreTest : TransportTest() {
protected val data = getRandomByteArray() protected val data = getRandomByteArray()
protected val inputStream = mockk<InputStream>() protected val inputStream = mockk<InputStream>()
protected val decryptedInputStream = mockk<InputStream>()
protected val unsupportedVersion = (VERSION + 1).toByte() protected val unsupportedVersion = (VERSION + 1).toByte()