summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarsh Shandilya <me@msfjarvis.dev>2022-01-21 00:27:04 +0530
committerGitHub <noreply@github.com>2022-01-21 00:27:04 +0530
commit78a90aacb3127ac2eee7be09e7fb3c7c74ff6174 (patch)
tree0567524830f1bae2e119bec17f017b89f27c54fd
parent541bf62038c367dedc0fbee074fdb8bbcc46dd89 (diff)
Add support for properly dealing with incorrect passwords (#1672)
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/data/crypto/CryptoRepository.kt2
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/crypto/DecryptActivityV2.kt59
-rw-r--r--app/src/main/java/dev/msfjarvis/aps/ui/crypto/PasswordDialog.kt34
-rw-r--r--app/src/main/res/layout/dialog_password_entry.xml1
4 files changed, 72 insertions, 24 deletions
diff --git a/app/src/main/java/dev/msfjarvis/aps/data/crypto/CryptoRepository.kt b/app/src/main/java/dev/msfjarvis/aps/data/crypto/CryptoRepository.kt
index b0cde380..2923b117 100644
--- a/app/src/main/java/dev/msfjarvis/aps/data/crypto/CryptoRepository.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/data/crypto/CryptoRepository.kt
@@ -42,7 +42,7 @@ constructor(
) {
val keys = pgpKeyManager.getAllKeys().unwrap()
// Iterates through the keys until the first successful decryption, then returns.
- keys.first { key ->
+ keys.firstOrNull { key ->
runCatching { pgpCryptoHandler.decrypt(key, password, message, out) }.isOk()
}
}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/crypto/DecryptActivityV2.kt b/app/src/main/java/dev/msfjarvis/aps/ui/crypto/DecryptActivityV2.kt
index 33ecdc2b..5b84478e 100644
--- a/app/src/main/java/dev/msfjarvis/aps/ui/crypto/DecryptActivityV2.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/crypto/DecryptActivityV2.kt
@@ -10,6 +10,7 @@ import android.os.Bundle
import android.view.Menu
import android.view.MenuItem
import androidx.lifecycle.lifecycleScope
+import com.github.michaelbull.result.runCatching
import dagger.hilt.android.AndroidEntryPoint
import dev.msfjarvis.aps.R
import dev.msfjarvis.aps.data.crypto.CryptoRepository
@@ -17,6 +18,7 @@ import dev.msfjarvis.aps.data.passfile.PasswordEntry
import dev.msfjarvis.aps.data.password.FieldItem
import dev.msfjarvis.aps.databinding.DecryptLayoutBinding
import dev.msfjarvis.aps.ui.adapters.FieldItemAdapter
+import dev.msfjarvis.aps.util.extensions.isErr
import dev.msfjarvis.aps.util.extensions.unsafeLazy
import dev.msfjarvis.aps.util.extensions.viewBinding
import dev.msfjarvis.aps.util.settings.PreferenceKeys
@@ -42,6 +44,7 @@ class DecryptActivityV2 : BasePgpActivity() {
private val relativeParentPath by unsafeLazy { getParentPath(fullPath, repoPath) }
private var passwordEntry: PasswordEntry? = null
+ private var retries = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@@ -56,7 +59,7 @@ class DecryptActivityV2 : BasePgpActivity() {
true
}
}
- decrypt()
+ decrypt(isError = false)
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
@@ -126,36 +129,51 @@ class DecryptActivityV2 : BasePgpActivity() {
)
}
- private fun decrypt() {
+ private fun decrypt(isError: Boolean) {
+ if (retries < MAX_RETRIES) {
+ retries += 1
+ } else {
+ finish()
+ }
val dialog = PasswordDialog()
+ if (isError) {
+ dialog.setError()
+ }
lifecycleScope.launch(Dispatchers.Main) {
dialog.password.collectLatest { value ->
if (value != null) {
- decrypt(value)
+ if (runCatching { decrypt(value) }.isErr()) {
+ decrypt(isError = true)
+ }
}
}
}
dialog.show(supportFragmentManager, "PASSWORD_DIALOG")
}
- private fun decrypt(password: String) {
- lifecycleScope.launch {
- val message = withContext(Dispatchers.IO) { File(fullPath).readBytes().inputStream() }
- val result =
- withContext(Dispatchers.IO) {
- val outputStream = ByteArrayOutputStream()
- repository.decrypt(
- password,
- message,
- outputStream,
- )
- outputStream
- }
- startAutoDismissTimer()
+ private suspend fun decrypt(password: String) {
+ val message = withContext(Dispatchers.IO) { File(fullPath).readBytes().inputStream() }
+ val result =
+ withContext(Dispatchers.IO) {
+ val outputStream = ByteArrayOutputStream()
+ repository.decrypt(
+ password,
+ message,
+ outputStream,
+ )
+ outputStream
+ }
+ require(result.size() != 0) { "Incorrect password" }
+ startAutoDismissTimer()
+
+ val entry = passwordEntryFactory.create(lifecycleScope, result.toByteArray())
+ passwordEntry = entry
+ createPasswordUi(entry)
+ }
+ private suspend fun createPasswordUi(entry: PasswordEntry) =
+ withContext(Dispatchers.Main) {
val showPassword = settings.getBoolean(PreferenceKeys.SHOW_PASSWORD, true)
- val entry = passwordEntryFactory.create(lifecycleScope, result.toByteArray())
- passwordEntry = entry
invalidateOptionsMenu()
val items = arrayListOf<FieldItem>()
@@ -187,5 +205,8 @@ class DecryptActivityV2 : BasePgpActivity() {
}
}
}
+
+ private companion object {
+ private const val MAX_RETRIES = 3
}
}
diff --git a/app/src/main/java/dev/msfjarvis/aps/ui/crypto/PasswordDialog.kt b/app/src/main/java/dev/msfjarvis/aps/ui/crypto/PasswordDialog.kt
index 3542422a..d69a4686 100644
--- a/app/src/main/java/dev/msfjarvis/aps/ui/crypto/PasswordDialog.kt
+++ b/app/src/main/java/dev/msfjarvis/aps/ui/crypto/PasswordDialog.kt
@@ -8,6 +8,9 @@ package dev.msfjarvis.aps.ui.crypto
import android.app.Dialog
import android.content.DialogInterface
import android.os.Bundle
+import android.view.KeyEvent
+import android.view.WindowManager
+import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.DialogFragment
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dev.msfjarvis.aps.R
@@ -21,6 +24,7 @@ import kotlinx.coroutines.flow.asStateFlow
class PasswordDialog : DialogFragment() {
private val binding by unsafeLazy { DialogPasswordEntryBinding.inflate(layoutInflater) }
+ private var isError: Boolean = false
private val _password = MutableStateFlow<String?>(null)
val password = _password.asStateFlow()
@@ -28,15 +32,37 @@ class PasswordDialog : DialogFragment() {
val builder = MaterialAlertDialogBuilder(requireContext())
builder.setView(binding.root)
builder.setTitle(R.string.password)
- builder.setPositiveButton(android.R.string.ok) { _, _ ->
- do {} while (!_password.tryEmit(binding.passwordEditText.text.toString()))
- dismiss()
+ builder.setPositiveButton(android.R.string.ok) { _, _ -> tryEmitPassword() }
+ val dialog = builder.create()
+ dialog.window?.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE)
+ dialog.setOnShowListener {
+ if (isError) {
+ binding.passwordField.error = getString(R.string.git_operation_wrong_password)
+ }
+ binding.passwordEditText.doOnTextChanged { _, _, _, _ -> binding.passwordField.error = null }
+ binding.passwordEditText.setOnKeyListener { _, keyCode, _ ->
+ if (keyCode == KeyEvent.KEYCODE_ENTER) {
+ tryEmitPassword()
+ return@setOnKeyListener true
+ }
+ false
+ }
}
- return builder.create()
+ return dialog
+ }
+
+ fun setError() {
+ isError = true
}
override fun onCancel(dialog: DialogInterface) {
super.onCancel(dialog)
finish()
}
+
+ @Suppress("ControlFlowWithEmptyBody")
+ private fun tryEmitPassword() {
+ do {} while (!_password.tryEmit(binding.passwordEditText.text.toString()))
+ dismissAllowingStateLoss()
+ }
}
diff --git a/app/src/main/res/layout/dialog_password_entry.xml b/app/src/main/res/layout/dialog_password_entry.xml
index 3729fb4f..0072d491 100644
--- a/app/src/main/res/layout/dialog_password_entry.xml
+++ b/app/src/main/res/layout/dialog_password_entry.xml
@@ -24,6 +24,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword" />
+ <requestFocus />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>