refactor: improve passphrase cache save logic

This commit is contained in:
Harsh Shandilya 2024-03-12 13:05:55 +05:30
parent 5082df2f93
commit 69bdd1518c

View file

@ -19,7 +19,7 @@ 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.auth.BiometricAuthenticator import app.passwordstore.util.auth.BiometricAuthenticator
import app.passwordstore.util.auth.BiometricAuthenticator.Result import app.passwordstore.util.auth.BiometricAuthenticator.Result as BiometricResult
import app.passwordstore.util.extensions.getString import app.passwordstore.util.extensions.getString
import app.passwordstore.util.extensions.unsafeLazy import app.passwordstore.util.extensions.unsafeLazy
import app.passwordstore.util.extensions.viewBinding import app.passwordstore.util.extensions.viewBinding
@ -78,7 +78,7 @@ class DecryptActivity : BasePGPActivity() {
requireKeysExist { decrypt(isError = false, authResult) } requireKeysExist { decrypt(isError = false, authResult) }
} }
} else { } else {
requireKeysExist { decrypt(isError = false, Result.CanceledByUser) } requireKeysExist { decrypt(isError = false, BiometricResult.CanceledByUser) }
} }
} }
@ -151,24 +151,24 @@ class DecryptActivity : BasePGPActivity() {
) )
} }
private fun decrypt(isError: Boolean, authResult: Result) { private fun decrypt(isError: Boolean, authResult: BiometricResult) {
val gpgIdentifiers = getPGPIdentifiers("") ?: return val gpgIdentifiers = getPGPIdentifiers("") ?: return
lifecycleScope.launch(dispatcherProvider.main()) { lifecycleScope.launch(dispatcherProvider.main()) {
when (authResult) { when (authResult) {
// Internally handled by the prompt dialog // Internally handled by the prompt dialog
is Result.Retry -> {} is BiometricResult.Retry -> {}
// If the dialog is dismissed for any reason, prompt for passphrase // If the dialog is dismissed for any reason, prompt for passphrase
is Result.CanceledByUser, is BiometricResult.CanceledByUser,
is Result.CanceledBySystem, is BiometricResult.CanceledBySystem,
is Result.Failure, is BiometricResult.Failure,
is Result.HardwareUnavailableOrDisabled -> is BiometricResult.HardwareUnavailableOrDisabled ->
askPassphrase(isError, gpgIdentifiers, authResult) askPassphrase(isError, gpgIdentifiers, authResult)
// //
is Result.Success -> { is BiometricResult.Success -> {
val cachedPassphrase = val cachedPassphrase =
passphraseCache.retrieveCachedPassphrase(this@DecryptActivity, gpgIdentifiers.first()) passphraseCache.retrieveCachedPassphrase(this@DecryptActivity, gpgIdentifiers.first())
if (cachedPassphrase != null) { if (cachedPassphrase != null) {
decryptWithCachedPassphrase(cachedPassphrase, gpgIdentifiers, authResult) decryptWithPassphrase(cachedPassphrase, gpgIdentifiers, authResult)
} else { } else {
askPassphrase(isError, gpgIdentifiers, authResult) askPassphrase(isError, gpgIdentifiers, authResult)
} }
@ -180,7 +180,7 @@ class DecryptActivity : BasePGPActivity() {
private fun askPassphrase( private fun askPassphrase(
isError: Boolean, isError: Boolean,
gpgIdentifiers: List<PGPIdentifier>, gpgIdentifiers: List<PGPIdentifier>,
authResult: Result, authResult: BiometricResult,
) { ) {
if (retries < MAX_RETRIES) { if (retries < MAX_RETRIES) {
retries += 1 retries += 1
@ -194,39 +194,33 @@ class DecryptActivity : BasePGPActivity() {
dialog.show(supportFragmentManager, "PASSWORD_DIALOG") dialog.show(supportFragmentManager, "PASSWORD_DIALOG")
dialog.setFragmentResultListener(PasswordDialog.PASSWORD_RESULT_KEY) { key, bundle -> dialog.setFragmentResultListener(PasswordDialog.PASSWORD_RESULT_KEY) { key, bundle ->
if (key == PasswordDialog.PASSWORD_RESULT_KEY) { if (key == PasswordDialog.PASSWORD_RESULT_KEY) {
val value = bundle.getString(PasswordDialog.PASSWORD_RESULT_KEY)!! val passphrase = bundle.getString(PasswordDialog.PASSWORD_RESULT_KEY)!!
lifecycleScope.launch(dispatcherProvider.main()) { lifecycleScope.launch(dispatcherProvider.main()) {
when (val result = decryptWithPassphrase(value, gpgIdentifiers)) { decryptWithPassphrase(passphrase, gpgIdentifiers, authResult) {
is Ok -> { passphraseCache.cachePassphrase(
val entry = passwordEntryFactory.create(result.value.toByteArray()) this@DecryptActivity,
passwordEntry = entry gpgIdentifiers.first(),
createPasswordUI(entry) passphrase
startAutoDismissTimer() )
if (authResult is Result.Success) {
passphraseCache.cachePassphrase(this@DecryptActivity, gpgIdentifiers.first(), value)
}
}
is Err -> {
logcat(ERROR) { result.error.stackTraceToString() }
askPassphrase(isError = true, gpgIdentifiers, authResult)
}
} }
} }
} }
} }
} }
private suspend fun decryptWithCachedPassphrase( private suspend fun decryptWithPassphrase(
passphrase: String, passphrase: String,
identifiers: List<PGPIdentifier>, identifiers: List<PGPIdentifier>,
authResult: Result, authResult: BiometricResult,
onSuccess: suspend () -> Unit = {},
) { ) {
when (val result = decryptWithPassphrase(passphrase, identifiers)) { when (val result = decryptPGPStream(passphrase, identifiers)) {
is Ok -> { is Ok -> {
val entry = passwordEntryFactory.create(result.value.toByteArray()) val entry = passwordEntryFactory.create(result.value.toByteArray())
passwordEntry = entry passwordEntry = entry
createPasswordUI(entry) createPasswordUI(entry)
startAutoDismissTimer() startAutoDismissTimer()
onSuccess()
} }
is Err -> { is Err -> {
logcat(ERROR) { result.error.stackTraceToString() } logcat(ERROR) { result.error.stackTraceToString() }
@ -235,15 +229,15 @@ class DecryptActivity : BasePGPActivity() {
} }
} }
private suspend fun decryptWithPassphrase( private suspend fun decryptPGPStream(
password: String, passphrase: String,
gpgIdentifiers: List<PGPIdentifier>, gpgIdentifiers: List<PGPIdentifier>,
) = runCatching { ) = runCatching {
val message = withContext(dispatcherProvider.io()) { File(fullPath).readBytes().inputStream() } val message = withContext(dispatcherProvider.io()) { File(fullPath).readBytes().inputStream() }
val outputStream = ByteArrayOutputStream() val outputStream = ByteArrayOutputStream()
val result = val result =
repository.decrypt( repository.decrypt(
password, passphrase,
gpgIdentifiers, gpgIdentifiers,
message, message,
outputStream, outputStream,