parent
fcf17fe23a
commit
a98364efbe
3 changed files with 24 additions and 15 deletions
|
@ -1,6 +1,5 @@
|
|||
package com.stevesoltys.seedvault.crypto
|
||||
|
||||
import android.os.Build.VERSION.SDK_INT
|
||||
import android.security.keystore.KeyProperties.BLOCK_MODE_GCM
|
||||
import android.security.keystore.KeyProperties.ENCRYPTION_PADDING_NONE
|
||||
import android.security.keystore.KeyProperties.PURPOSE_DECRYPT
|
||||
|
@ -48,7 +47,7 @@ internal class KeyManagerImpl : KeyManager {
|
|||
|
||||
override fun storeBackupKey(seed: ByteArray) {
|
||||
if (seed.size < KEY_SIZE_BYTES) throw IllegalArgumentException()
|
||||
// TODO check if using first 256 of 512 bytes produced by PBKDF2WithHmacSHA512 is safe!
|
||||
// TODO check if using first 256 of 512 bits produced by PBKDF2WithHmacSHA512 is safe!
|
||||
val secretKeySpec = SecretKeySpec(seed, 0, KEY_SIZE_BYTES, "AES")
|
||||
val ksEntry = SecretKeyEntry(secretKeySpec)
|
||||
keyStore.setEntry(KEY_ALIAS, ksEntry, getKeyProtection())
|
||||
|
@ -68,7 +67,7 @@ internal class KeyManagerImpl : KeyManager {
|
|||
.setEncryptionPaddings(ENCRYPTION_PADDING_NONE)
|
||||
.setRandomizedEncryptionRequired(true)
|
||||
// unlocking is required only for decryption, so when restoring from backup
|
||||
if (SDK_INT >= 28) builder.setUnlockedDeviceRequired(true)
|
||||
builder.setUnlockedDeviceRequired(true)
|
||||
return builder.build()
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import android.app.backup.BackupTransport.TRANSPORT_PACKAGE_REJECTED
|
|||
import android.app.backup.BackupTransport.TRANSPORT_QUOTA_EXCEEDED
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageInfo
|
||||
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
|
||||
import android.os.ParcelFileDescriptor
|
||||
import android.util.Log
|
||||
import com.stevesoltys.seedvault.BackupNotificationManager
|
||||
|
@ -94,7 +95,20 @@ internal class BackupCoordinator(
|
|||
return targetPackage.packageName != plugin.providerPackageName
|
||||
}
|
||||
|
||||
/**
|
||||
* Ask the transport about current quota for backup size of the package.
|
||||
*
|
||||
* @param packageName ID of package to provide the quota.
|
||||
* @param isFullBackup If set, transport should return limit for full data backup,
|
||||
* otherwise for key-value backup.
|
||||
* @return Current limit on backup size in bytes.
|
||||
*/
|
||||
fun getBackupQuota(packageName: String, isFullBackup: Boolean): Long {
|
||||
// try to back up APK here as later methods are sometimes not called called
|
||||
val pm = context.packageManager
|
||||
backUpApk(pm.getPackageInfo(packageName, GET_SIGNING_CERTIFICATES))
|
||||
|
||||
// report back quota
|
||||
Log.i(TAG, "Get backup quota for $packageName. Is full backup: $isFullBackup.")
|
||||
val quota = if (isFullBackup) full.getQuota() else kv.getQuota()
|
||||
Log.i(TAG, "Reported quota of $quota bytes.")
|
||||
|
@ -132,8 +146,7 @@ internal class BackupCoordinator(
|
|||
// hook in here to back up APKs of apps that are otherwise not allowed for backup
|
||||
backUpNotAllowedPackages()
|
||||
}
|
||||
val result = kv.performBackup(packageInfo, data, flags)
|
||||
return backUpApk(result, packageInfo)
|
||||
return kv.performBackup(packageInfo, data, flags)
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------
|
||||
|
@ -166,8 +179,7 @@ internal class BackupCoordinator(
|
|||
|
||||
fun performFullBackup(targetPackage: PackageInfo, fileDescriptor: ParcelFileDescriptor, flags: Int): Int {
|
||||
cancelReason = UNKNOWN_ERROR
|
||||
val result = full.performFullBackup(targetPackage, fileDescriptor, flags)
|
||||
return backUpApk(result, targetPackage)
|
||||
return full.performFullBackup(targetPackage, fileDescriptor, flags)
|
||||
}
|
||||
|
||||
fun sendBackupData(numBytes: Int) = full.sendBackupData(numBytes)
|
||||
|
@ -254,27 +266,24 @@ internal class BackupCoordinator(
|
|||
Log.d(TAG, "Checking if APKs of opt-out apps need backup...")
|
||||
packageService.notAllowedPackages.forEach { optOutPackageInfo ->
|
||||
try {
|
||||
backUpApk(0, optOutPackageInfo, NOT_ALLOWED)
|
||||
backUpApk(optOutPackageInfo, NOT_ALLOWED)
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Error backing up opt-out APK of ${optOutPackageInfo.packageName}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun backUpApk(result: Int, packageInfo: PackageInfo, packageState: PackageState = UNKNOWN_ERROR): Int {
|
||||
private fun backUpApk(packageInfo: PackageInfo, packageState: PackageState = UNKNOWN_ERROR) {
|
||||
val packageName = packageInfo.packageName
|
||||
if (packageName == MAGIC_PACKAGE_MANAGER) return result
|
||||
return try {
|
||||
try {
|
||||
apkBackup.backupApkIfNecessary(packageInfo, packageState) {
|
||||
plugin.getApkOutputStream(packageInfo)
|
||||
}?.let { packageMetadata ->
|
||||
val outputStream = plugin.getMetadataOutputStream()
|
||||
metadataManager.onApkBackedUp(packageInfo, packageMetadata, outputStream)
|
||||
}
|
||||
result
|
||||
} catch (e: IOException) {
|
||||
Log.e(TAG, "Error while writing APK or metadata for $packageName", e)
|
||||
TRANSPORT_PACKAGE_REJECTED
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,6 +113,7 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
val isFullBackup = Random.nextBoolean()
|
||||
val quota = Random.nextLong()
|
||||
|
||||
expectApkBackupAndMetadataWrite()
|
||||
if (isFullBackup) {
|
||||
every { full.getQuota() } returns quota
|
||||
} else {
|
||||
|
@ -264,9 +265,9 @@ internal class BackupCoordinatorTest : BackupTest() {
|
|||
}
|
||||
|
||||
private fun expectApkBackupAndMetadataWrite() {
|
||||
every { apkBackup.backupApkIfNecessary(packageInfo, UNKNOWN_ERROR, any()) } returns packageMetadata
|
||||
every { apkBackup.backupApkIfNecessary(any(), UNKNOWN_ERROR, any()) } returns packageMetadata
|
||||
every { plugin.getMetadataOutputStream() } returns metadataOutputStream
|
||||
every { metadataManager.onApkBackedUp(packageInfo, packageMetadata, metadataOutputStream) } just Runs
|
||||
every { metadataManager.onApkBackedUp(any(), packageMetadata, metadataOutputStream) } just Runs
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue