clear passphrase cache, fix application crash on auto screen-off (#3108)

clear passphrase chache on screen-off
This commit is contained in:
agrahn 2024-07-02 11:37:39 +02:00 committed by GitHub
parent 2820f0ed63
commit 66675864ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 32 additions and 22 deletions

View file

@ -30,10 +30,6 @@ import io.sentry.Sentry
import io.sentry.protocol.User import io.sentry.protocol.User
import java.util.concurrent.Executors import java.util.concurrent.Executors
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import logcat.AndroidLogcatLogger import logcat.AndroidLogcatLogger
import logcat.LogPriority.DEBUG import logcat.LogPriority.DEBUG
import logcat.LogPriority.VERBOSE import logcat.LogPriority.VERBOSE
@ -76,27 +72,18 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere
} }
scope.user = user scope.user = user
} }
setupPassphraseCacheClearAction() setupScreenOffHandler()
} }
@OptIn(DelicateCoroutinesApi::class) private fun setupScreenOffHandler() {
private fun setupPassphraseCacheClearAction() { val screenOffReceiver: BroadcastReceiver =
if (prefs.getBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, false)) { object : BroadcastReceiver() {
val screenOffReceiver: BroadcastReceiver = override fun onReceive(context: Context, intent: Intent) {
object : BroadcastReceiver() { if (intent.action == Intent.ACTION_SCREEN_OFF) screenWasOff = true
override fun onReceive(context: Context, intent: Intent) {
if (intent.action == Intent.ACTION_SCREEN_OFF) {
GlobalScope.launch {
withContext(dispatcherProvider.main()) {
passphraseCache.clearAllCachedPassphrases(context)
}
}
}
}
} }
val filter = IntentFilter(Intent.ACTION_SCREEN_OFF) }
registerReceiver(screenOffReceiver, filter) val filter = IntentFilter(Intent.ACTION_SCREEN_OFF)
} registerReceiver(screenOffReceiver, filter)
} }
override fun onTerminate() { override fun onTerminate() {
@ -159,5 +146,6 @@ class Application : android.app.Application(), SharedPreferences.OnSharedPrefere
companion object { companion object {
lateinit var instance: Application lateinit var instance: Application
var screenWasOff: Boolean = true
} }
} }

View file

@ -11,6 +11,7 @@ import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import androidx.fragment.app.setFragmentResultListener import androidx.fragment.app.setFragmentResultListener
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import app.passwordstore.Application.Companion.screenWasOff
import app.passwordstore.R import app.passwordstore.R
import app.passwordstore.crypto.PGPIdentifier import app.passwordstore.crypto.PGPIdentifier
import app.passwordstore.crypto.errors.CryptoHandlerException import app.passwordstore.crypto.errors.CryptoHandlerException
@ -167,6 +168,11 @@ class DecryptActivity : BasePGPActivity() {
askPassphrase(isError, gpgIdentifiers, authResult) askPassphrase(isError, gpgIdentifiers, authResult)
// //
is BiometricResult.Success -> { is BiometricResult.Success -> {
// clear passphrase cache on first use after application startup or if screen was off
if (screenWasOff && settings.getBoolean(PreferenceKeys.CLEAR_PASSPHRASE_CACHE, false)) {
passphraseCache.clearAllCachedPassphrases(this@DecryptActivity)
screenWasOff = false
}
val cachedPassphrase = val cachedPassphrase =
passphraseCache.retrieveCachedPassphrase(this@DecryptActivity, gpgIdentifiers.first()) passphraseCache.retrieveCachedPassphrase(this@DecryptActivity, gpgIdentifiers.first())
if (cachedPassphrase != null) { if (cachedPassphrase != null) {

View file

@ -65,6 +65,22 @@ class PGPSettings(
titleRes = R.string.pref_passphrase_cache_auto_clear_title titleRes = R.string.pref_passphrase_cache_auto_clear_title
summaryRes = R.string.pref_passphrase_cache_auto_clear_summary summaryRes = R.string.pref_passphrase_cache_auto_clear_summary
defaultValue = false defaultValue = false
/* clear cache once when unchecking; this is to prevent a malicious user
* from bypassing cache clearing via the settings */
onCheckedChange { checked ->
if (!checked && BiometricAuthenticator.canAuthenticate(activity)) {
BiometricAuthenticator.authenticate(
activity,
R.string.pref_passphrase_cache_authenticate_clear,
) {
if (it is BiometricAuthenticator.Result.Success)
activity.lifecycleScope.launch {
passphraseCache.clearAllCachedPassphrases(activity)
}
}
}
true
}
} }
} }
} }