Improve error message when no backups could be found for restore
This commit is contained in:
parent
54ad762eb1
commit
10ad6d6b2d
7 changed files with 20 additions and 22 deletions
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue