Improve error message when no backups could be found for restore

This commit is contained in:
Torsten Grote 2019-09-13 15:23:48 -03:00
parent 54ad762eb1
commit 10ad6d6b2d
No known key found for this signature in database
GPG key ID: 3E5F77D92CF891FF
7 changed files with 20 additions and 22 deletions

View file

@ -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)

View file

@ -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)
}
}

View file

@ -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

View file

@ -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)

View file

@ -54,7 +54,8 @@
<TextView
android:id="@+id/errorView"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_margin="16dp"
android:layout_height="wrap_content"
android:textColor="@android:color/holo_red_dark"
android:textSize="18sp"
@ -63,7 +64,8 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/titleView"
tools:text="There was an error retrieving your backups." />
tools:text="@string/restore_set_empty_result"
tools:visibility="visible" />
<TextView
android:id="@+id/backView"

View file

@ -90,8 +90,8 @@
<string name="restore_back">Don\'t restore</string>
<string name="restore_invalid_location_title">No backups found</string>
<string name="restore_invalid_location_message">We could not find any backups at this location.\n\nPlease choose another location that contains a %s folder.</string>
<string name="restore_set_error">An error occurred loading the backups.</string>
<string name="restore_set_empty_result">No backups found at given location.</string>
<string name="restore_set_error">An error occurred while loading the backups.</string>
<string name="restore_set_empty_result">No suitable backups found at given location.\n\nThis is most likely due to a wrong recovery code or a storage error.</string>
<string name="restore_restoring">Restoring Backup</string>
<string name="restore_current_package">Restoring %s…</string>
<string name="restore_finished_success">Restore complete.</string>

View file

@ -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)
}
}