fix(app): ensure decryption errors are captured by UI

This commit is contained in:
Harsh Shandilya 2022-10-07 19:41:14 +05:30
parent f778eab94e
commit b313c4216e
No known key found for this signature in database
2 changed files with 36 additions and 35 deletions

View file

@ -8,6 +8,8 @@ package app.passwordstore.data.crypto
import app.passwordstore.crypto.GpgIdentifier import app.passwordstore.crypto.GpgIdentifier
import app.passwordstore.crypto.PGPKeyManager import app.passwordstore.crypto.PGPKeyManager
import app.passwordstore.crypto.PGPainlessCryptoHandler import app.passwordstore.crypto.PGPainlessCryptoHandler
import app.passwordstore.crypto.errors.CryptoHandlerException
import com.github.michaelbull.result.Result
import com.github.michaelbull.result.getAll import com.github.michaelbull.result.getAll
import com.github.michaelbull.result.unwrap import com.github.michaelbull.result.unwrap
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
@ -27,34 +29,30 @@ constructor(
password: String, password: String,
message: ByteArrayInputStream, message: ByteArrayInputStream,
out: ByteArrayOutputStream, out: ByteArrayOutputStream,
) { ) = withContext(Dispatchers.IO) { decryptPgp(password, message, out) }
withContext(Dispatchers.IO) { decryptPgp(password, message, out) }
}
suspend fun encrypt( suspend fun encrypt(
identities: List<GpgIdentifier>, identities: List<GpgIdentifier>,
content: ByteArrayInputStream, content: ByteArrayInputStream,
out: ByteArrayOutputStream, out: ByteArrayOutputStream,
) { ) = withContext(Dispatchers.IO) { encryptPgp(identities, content, out) }
withContext(Dispatchers.IO) { encryptPgp(identities, content, out) }
}
private suspend fun decryptPgp( private suspend fun decryptPgp(
password: String, password: String,
message: ByteArrayInputStream, message: ByteArrayInputStream,
out: ByteArrayOutputStream, out: ByteArrayOutputStream,
) { ): Result<Unit, CryptoHandlerException> {
val keys = pgpKeyManager.getAllKeys().unwrap() val keys = pgpKeyManager.getAllKeys().unwrap()
pgpCryptoHandler.decrypt(keys, password, message, out) return pgpCryptoHandler.decrypt(keys, password, message, out)
} }
private suspend fun encryptPgp( private suspend fun encryptPgp(
identities: List<GpgIdentifier>, identities: List<GpgIdentifier>,
content: ByteArrayInputStream, content: ByteArrayInputStream,
out: ByteArrayOutputStream, out: ByteArrayOutputStream,
) { ): Result<Unit, CryptoHandlerException> {
val keys = identities.map { ident -> pgpKeyManager.getKeyById(ident) }.getAll() val keys = identities.map { id -> pgpKeyManager.getKeyById(id) }.getAll()
pgpCryptoHandler.encrypt( return pgpCryptoHandler.encrypt(
keys, keys,
content, content,
out, out,

View file

@ -17,13 +17,13 @@ import app.passwordstore.data.password.FieldItem
import app.passwordstore.databinding.DecryptLayoutBinding import app.passwordstore.databinding.DecryptLayoutBinding
import app.passwordstore.ui.adapters.FieldItemAdapter import app.passwordstore.ui.adapters.FieldItemAdapter
import app.passwordstore.util.extensions.getString import app.passwordstore.util.extensions.getString
import app.passwordstore.util.extensions.isErr
import app.passwordstore.util.extensions.unsafeLazy import app.passwordstore.util.extensions.unsafeLazy
import app.passwordstore.util.extensions.viewBinding import app.passwordstore.util.extensions.viewBinding
import app.passwordstore.util.settings.Constants import app.passwordstore.util.settings.Constants
import app.passwordstore.util.settings.PreferenceKeys import app.passwordstore.util.settings.PreferenceKeys
import com.github.michaelbull.result.Err
import com.github.michaelbull.result.Ok
import com.github.michaelbull.result.runCatching import com.github.michaelbull.result.runCatching
import com.github.michaelbull.result.unwrapError
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.File import java.io.File
@ -66,7 +66,7 @@ class DecryptActivity : BasePgpActivity() {
true true
} }
} }
decrypt(isError = false) askPassphrase(isError = false)
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -137,7 +137,7 @@ class DecryptActivity : BasePgpActivity() {
) )
} }
private fun decrypt(isError: Boolean) { private fun askPassphrase(isError: Boolean) {
if (retries < MAX_RETRIES) { if (retries < MAX_RETRIES) {
retries += 1 retries += 1
} else { } else {
@ -150,10 +150,17 @@ class DecryptActivity : BasePgpActivity() {
lifecycleScope.launch(Dispatchers.Main) { lifecycleScope.launch(Dispatchers.Main) {
dialog.password.collectLatest { value -> dialog.password.collectLatest { value ->
if (value != null) { if (value != null) {
val res = runCatching { decrypt(value) } when (val result = decryptWithPassphrase(value)) {
if (res.isErr()) { is Ok -> {
logcat(ERROR) { res.unwrapError().stackTraceToString() } val entry = passwordEntryFactory.create(result.value.toByteArray())
decrypt(isError = true) passwordEntry = entry
createPasswordUI(entry)
startAutoDismissTimer()
}
is Err -> {
logcat(ERROR) { result.error.stackTraceToString() }
askPassphrase(isError = true)
}
} }
} }
} }
@ -161,26 +168,22 @@ class DecryptActivity : BasePgpActivity() {
dialog.show(supportFragmentManager, "PASSWORD_DIALOG") dialog.show(supportFragmentManager, "PASSWORD_DIALOG")
} }
private suspend fun decrypt(password: String) { private suspend fun decryptWithPassphrase(password: String) = runCatching {
val message = withContext(Dispatchers.IO) { File(fullPath).readBytes().inputStream() } val message = withContext(Dispatchers.IO) { File(fullPath).readBytes().inputStream() }
val result =
withContext(Dispatchers.IO) {
val outputStream = ByteArrayOutputStream() val outputStream = ByteArrayOutputStream()
val result =
repository.decrypt( repository.decrypt(
password, password,
message, message,
outputStream, outputStream,
) )
outputStream when (result) {
is Ok -> outputStream
is Err -> throw result.error
} }
startAutoDismissTimer()
val entry = passwordEntryFactory.create(result.toByteArray())
passwordEntry = entry
createPasswordUi(entry)
} }
private suspend fun createPasswordUi(entry: PasswordEntry) = private suspend fun createPasswordUI(entry: PasswordEntry) =
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
val showPassword = settings.getBoolean(PreferenceKeys.SHOW_PASSWORD, true) val showPassword = settings.getBoolean(PreferenceKeys.SHOW_PASSWORD, true)
invalidateOptionsMenu() invalidateOptionsMenu()