Do K/V backup with new version 1 with new crypto
Restoring still supports version 0 with old crypto
This commit is contained in:
parent
3ffb79b04f
commit
0c3ea7679b
7 changed files with 105 additions and 119 deletions
|
@ -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 =
|
||||||
|
|
|
@ -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)
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue