Add support for properly dealing with incorrect passwords (#1672)
This commit is contained in:
parent
541bf62038
commit
78a90aacb3
4 changed files with 72 additions and 24 deletions
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,20 +129,29 @@ 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 {
|
||||
private suspend fun decrypt(password: String) {
|
||||
val message = withContext(Dispatchers.IO) { File(fullPath).readBytes().inputStream() }
|
||||
val result =
|
||||
withContext(Dispatchers.IO) {
|
||||
|
@ -151,11 +163,17 @@ class DecryptActivityV2 : BasePgpActivity() {
|
|||
)
|
||||
outputStream
|
||||
}
|
||||
require(result.size() != 0) { "Incorrect password" }
|
||||
startAutoDismissTimer()
|
||||
|
||||
val showPassword = settings.getBoolean(PreferenceKeys.SHOW_PASSWORD, true)
|
||||
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)
|
||||
invalidateOptionsMenu()
|
||||
|
||||
val items = arrayListOf<FieldItem>()
|
||||
|
@ -187,5 +205,8 @@ class DecryptActivityV2 : BasePgpActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
private const val MAX_RETRIES = 3
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
return builder.create()
|
||||
binding.passwordEditText.doOnTextChanged { _, _, _, _ -> binding.passwordField.error = null }
|
||||
binding.passwordEditText.setOnKeyListener { _, keyCode, _ ->
|
||||
if (keyCode == KeyEvent.KEYCODE_ENTER) {
|
||||
tryEmitPassword()
|
||||
return@setOnKeyListener true
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
Loading…
Reference in a new issue