From 10ad6d6b2dad0693c9a638734564d22ebb26d719 Mon Sep 17 00:00:00 2001 From: Torsten Grote Date: Fri, 13 Sep 2019 15:23:48 -0300 Subject: [PATCH] Improve error message when no backups could be found for restore --- .../java/com/stevesoltys/backup/metadata/Metadata.kt | 2 +- .../com/stevesoltys/backup/metadata/MetadataReader.kt | 11 +++++------ .../backup/transport/restore/RestoreCoordinator.kt | 8 ++++---- .../backup/ui/recoverycode/RecoveryCodeViewModel.kt | 3 --- app/src/main/res/layout/fragment_restore_set.xml | 6 ++++-- app/src/main/res/values/strings.xml | 4 ++-- .../stevesoltys/backup/metadata/MetadataReaderTest.kt | 8 ++++---- 7 files changed, 20 insertions(+), 22 deletions(-) diff --git a/app/src/main/java/com/stevesoltys/backup/metadata/Metadata.kt b/app/src/main/java/com/stevesoltys/backup/metadata/Metadata.kt index 8797a308..9cea9ff9 100644 --- a/app/src/main/java/com/stevesoltys/backup/metadata/Metadata.kt +++ b/app/src/main/java/com/stevesoltys/backup/metadata/Metadata.kt @@ -17,7 +17,7 @@ internal const val JSON_TOKEN = "token" internal const val JSON_ANDROID_VERSION = "androidVersion" internal const val JSON_DEVICE_NAME = "deviceName" -class FormatException(cause: Throwable) : Exception(cause) +class DecryptionFailedException(cause: Throwable) : Exception(cause) class EncryptedBackupMetadata private constructor(val token: Long, val inputStream: InputStream?, val error: Boolean) { constructor(token: Long, inputStream: InputStream) : this(token, inputStream, false) diff --git a/app/src/main/java/com/stevesoltys/backup/metadata/MetadataReader.kt b/app/src/main/java/com/stevesoltys/backup/metadata/MetadataReader.kt index 26059c64..33d0dc4b 100644 --- a/app/src/main/java/com/stevesoltys/backup/metadata/MetadataReader.kt +++ b/app/src/main/java/com/stevesoltys/backup/metadata/MetadataReader.kt @@ -13,14 +13,14 @@ import javax.crypto.AEADBadTagException interface MetadataReader { - @Throws(FormatException::class, SecurityException::class, UnsupportedVersionException::class, IOException::class) + @Throws(SecurityException::class, DecryptionFailedException::class, UnsupportedVersionException::class, IOException::class) fun readMetadata(inputStream: InputStream, expectedToken: Long): BackupMetadata } class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader { - @Throws(FormatException::class, SecurityException::class, UnsupportedVersionException::class, IOException::class) + @Throws(SecurityException::class, DecryptionFailedException::class, UnsupportedVersionException::class, IOException::class) override fun readMetadata(inputStream: InputStream, expectedToken: Long): BackupMetadata { val version = inputStream.read().toByte() if (version < 0) throw IOException() @@ -28,14 +28,13 @@ class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader { val metadataBytes = try { crypto.decryptSegment(inputStream) } catch (e: AEADBadTagException) { - // TODO use yet another exception? - throw SecurityException(e) + throw DecryptionFailedException(e) } return decode(metadataBytes, version, expectedToken) } @VisibleForTesting - @Throws(FormatException::class, SecurityException::class) + @Throws(SecurityException::class) internal fun decode(bytes: ByteArray, expectedVersion: Byte, expectedToken: Long): BackupMetadata { // NOTE: We don't do extensive validation of the parsed input here, // because it was encrypted with authentication, so we should be able to trust it. @@ -59,7 +58,7 @@ class MetadataReaderImpl(private val crypto: Crypto) : MetadataReader { deviceName = json.getString(JSON_DEVICE_NAME) ) } catch (e: JSONException) { - throw FormatException(e) + throw SecurityException(e) } } diff --git a/app/src/main/java/com/stevesoltys/backup/transport/restore/RestoreCoordinator.kt b/app/src/main/java/com/stevesoltys/backup/transport/restore/RestoreCoordinator.kt index 0abc9a6c..aa6334fb 100644 --- a/app/src/main/java/com/stevesoltys/backup/transport/restore/RestoreCoordinator.kt +++ b/app/src/main/java/com/stevesoltys/backup/transport/restore/RestoreCoordinator.kt @@ -10,7 +10,7 @@ import android.content.pm.PackageInfo import android.os.ParcelFileDescriptor import android.util.Log import com.stevesoltys.backup.header.UnsupportedVersionException -import com.stevesoltys.backup.metadata.FormatException +import com.stevesoltys.backup.metadata.DecryptionFailedException import com.stevesoltys.backup.metadata.MetadataReader import com.stevesoltys.backup.settings.getBackupToken import libcore.io.IoUtils.closeQuietly @@ -50,12 +50,12 @@ internal class RestoreCoordinator( } catch (e: IOException) { Log.e(TAG, "Error while getting restore sets", e) return null - } catch (e: FormatException) { - Log.e(TAG, "Error while getting restore sets", e) - return null } catch (e: SecurityException) { Log.e(TAG, "Error while getting restore sets", e) return null + } catch (e: DecryptionFailedException) { + Log.e(TAG, "Error while decrypting restore set", e) + continue } catch (e: UnsupportedVersionException) { Log.w(TAG, "Backup with unsupported version read", e) continue diff --git a/app/src/main/java/com/stevesoltys/backup/ui/recoverycode/RecoveryCodeViewModel.kt b/app/src/main/java/com/stevesoltys/backup/ui/recoverycode/RecoveryCodeViewModel.kt index 8fdd8736..a0bd0d0e 100644 --- a/app/src/main/java/com/stevesoltys/backup/ui/recoverycode/RecoveryCodeViewModel.kt +++ b/app/src/main/java/com/stevesoltys/backup/ui/recoverycode/RecoveryCodeViewModel.kt @@ -47,9 +47,6 @@ class RecoveryCodeViewModel(application: Application) : AndroidViewModel(applica } catch (e: InvalidWordCountException) { throw AssertionError(e) } - - // TODO if (isRestore) check if we can decrypt a backup - val mnemonic = input.joinToString(" ") val seed = SeedCalculator(JavaxPBKDF2WithHmacSHA512.INSTANCE).calculateSeed(mnemonic, "") Backup.keyManager.storeBackupKey(seed) diff --git a/app/src/main/res/layout/fragment_restore_set.xml b/app/src/main/res/layout/fragment_restore_set.xml index ea9708d2..d1e398e7 100644 --- a/app/src/main/res/layout/fragment_restore_set.xml +++ b/app/src/main/res/layout/fragment_restore_set.xml @@ -54,7 +54,8 @@ + tools:text="@string/restore_set_empty_result" + tools:visibility="visible" /> Don\'t restore No backups found We could not find any backups at this location.\n\nPlease choose another location that contains a %s folder. - An error occurred loading the backups. - No backups found at given location. + An error occurred while loading the backups. + No suitable backups found at given location.\n\nThis is most likely due to a wrong recovery code or a storage error. Restoring Backup Restoring %s… Restore complete. diff --git a/app/src/test/java/com/stevesoltys/backup/metadata/MetadataReaderTest.kt b/app/src/test/java/com/stevesoltys/backup/metadata/MetadataReaderTest.kt index 44e05149..47ac104a 100644 --- a/app/src/test/java/com/stevesoltys/backup/metadata/MetadataReaderTest.kt +++ b/app/src/test/java/com/stevesoltys/backup/metadata/MetadataReaderTest.kt @@ -47,21 +47,21 @@ class MetadataReaderTest { } @Test - fun `malformed JSON throws FormatException`() { - assertThrows(FormatException::class.java) { + fun `malformed JSON throws SecurityException`() { + assertThrows(SecurityException::class.java) { decoder.decode("{".toByteArray(Utf8), metadata.version, metadata.token) } } @Test - fun `missing fields throws FormatException`() { + fun `missing fields throws SecurityException`() { val json = JSONObject() json.put(JSON_VERSION, metadata.version.toInt()) json.put(JSON_TOKEN, metadata.token) json.put(JSON_ANDROID_VERSION, metadata.androidVersion) val jsonBytes = json.toString().toByteArray(Utf8) - assertThrows(FormatException::class.java) { + assertThrows(SecurityException::class.java) { decoder.decode(jsonBytes, metadata.version, metadata.token) } }