From 2525ca3fa1a7d2642347f3f564aa29c939bb4db7 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Mon, 5 Oct 2020 15:51:30 -0300 Subject: [PATCH] Pull out code in ApkBackup and ApkRestore into own methods --- .../seedvault/transport/backup/ApkBackup.kt | 69 ++++++++++++------- .../seedvault/transport/restore/ApkRestore.kt | 18 +---- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt index 92ed5e38..f25f622a 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/backup/ApkBackup.kt @@ -14,13 +14,16 @@ import com.stevesoltys.seedvault.metadata.PackageMetadata import com.stevesoltys.seedvault.metadata.PackageState import com.stevesoltys.seedvault.settings.SettingsManager import java.io.File +import java.io.FileInputStream import java.io.FileNotFoundException import java.io.IOException +import java.io.InputStream import java.io.OutputStream import java.security.MessageDigest private val TAG = ApkBackup::class.java.simpleName +@Suppress("BlockingMethodInNonBlockingContext") class ApkBackup( private val pm: PackageManager, private val settingsManager: SettingsManager, @@ -87,31 +90,10 @@ class ApkBackup( } // get an InputStream for the APK - val apk = File(packageInfo.applicationInfo.sourceDir) - val inputStream = try { - apk.inputStream() - } catch (e: FileNotFoundException) { - Log.e(TAG, "Error opening ${apk.absolutePath} for backup.", e) - throw IOException(e) - } catch (e: SecurityException) { - Log.e(TAG, "Error opening ${apk.absolutePath} for backup.", e) - throw IOException(e) - } - + val inputStream = getApkInputStream(packageInfo.applicationInfo.sourceDir) // copy the APK to the storage's output and calculate SHA-256 hash while at it - val messageDigest = MessageDigest.getInstance("SHA-256") - streamGetter().use { outputStream -> - inputStream.use { inputStream -> - val buffer = ByteArray(DEFAULT_BUFFER_SIZE) - var bytes = inputStream.read(buffer) - while (bytes >= 0) { - outputStream.write(buffer, 0, bytes) - messageDigest.update(buffer, 0, bytes) - bytes = inputStream.read(buffer) - } - } - } - val sha256 = messageDigest.digest().encodeBase64() + val sha256 = copyStreamsAndGetHash(inputStream, streamGetter()) + Log.d(TAG, "Backed up new APK of $packageName with version $version.") // return updated metadata @@ -134,6 +116,45 @@ class ApkBackup( return packageMetadata.signatures.intersect(signatures).isEmpty() } + @Throws(IOException::class) + private fun getApkInputStream(apkPath: String): FileInputStream { + val apk = File(apkPath) + return try { + apk.inputStream() + } catch (e: FileNotFoundException) { + Log.e(TAG, "Error opening ${apk.absolutePath} for backup.", e) + throw IOException(e) + } catch (e: SecurityException) { + Log.e(TAG, "Error opening ${apk.absolutePath} for backup.", e) + throw IOException(e) + } + } + +} + +/** + * Copy the APK from the given [InputStream] to the given [OutputStream] + * and calculate the SHA-256 hash while at it. + * + * Both streams will be closed when the method returns. + * + * @return the APK's SHA-256 hash in Base64 format. + */ +@Throws(IOException::class) +fun copyStreamsAndGetHash(inputStream: InputStream, outputStream: OutputStream): String { + val messageDigest = MessageDigest.getInstance("SHA-256") + outputStream.use { oStream -> + inputStream.use { inputStream -> + val buffer = ByteArray(DEFAULT_BUFFER_SIZE) + var bytes = inputStream.read(buffer) + while (bytes >= 0) { + oStream.write(buffer, 0, bytes) + messageDigest.update(buffer, 0, bytes) + bytes = inputStream.read(buffer) + } + } + } + return messageDigest.digest().encodeBase64() } /** diff --git a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/ApkRestore.kt b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/ApkRestore.kt index a55eb28f..2a0acefb 100644 --- a/app/src/main/java/com/stevesoltys/seedvault/transport/restore/ApkRestore.kt +++ b/app/src/main/java/com/stevesoltys/seedvault/transport/restore/ApkRestore.kt @@ -6,9 +6,9 @@ import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES import android.content.pm.PackageManager.NameNotFoundException import android.graphics.drawable.Drawable import android.util.Log -import com.stevesoltys.seedvault.encodeBase64 import com.stevesoltys.seedvault.metadata.PackageMetadata import com.stevesoltys.seedvault.metadata.PackageMetadataMap +import com.stevesoltys.seedvault.transport.backup.copyStreamsAndGetHash import com.stevesoltys.seedvault.transport.backup.getSignatures import com.stevesoltys.seedvault.transport.backup.isSystemApp import com.stevesoltys.seedvault.transport.restore.ApkRestoreStatus.FAILED @@ -20,7 +20,6 @@ import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.flow import java.io.File import java.io.IOException -import java.security.MessageDigest import java.util.concurrent.ConcurrentHashMap private val TAG = ApkRestore::class.java.simpleName @@ -80,21 +79,10 @@ internal class ApkRestore( // create a cache file to write the APK into val cachedApk = File.createTempFile(packageName, ".apk", context.cacheDir) // copy APK to cache file and calculate SHA-256 hash while we are at it - val messageDigest = MessageDigest.getInstance("SHA-256") - restorePlugin.getApkInputStream(token, packageName).use { inputStream -> - cachedApk.outputStream().use { outputStream -> - val buffer = ByteArray(DEFAULT_BUFFER_SIZE) - var bytes = inputStream.read(buffer) - while (bytes >= 0) { - outputStream.write(buffer, 0, bytes) - messageDigest.update(buffer, 0, bytes) - bytes = inputStream.read(buffer) - } - } - } + val inputStream = restorePlugin.getApkInputStream(token, packageName) + val sha256 = copyStreamsAndGetHash(inputStream, cachedApk.outputStream()) // check APK's SHA-256 hash - val sha256 = messageDigest.digest().encodeBase64() if (metadata.sha256 != sha256) throw SecurityException( "Package $packageName has sha256 '$sha256', but '${metadata.sha256}' expected." )