aboutsummaryrefslogtreecommitdiff
path: root/app/src
diff options
context:
space:
mode:
Diffstat (limited to 'app/src')
-rw-r--r--app/src/main/java/app/passwordstore/data/crypto/GPGPassphraseCache.kt67
1 files changed, 67 insertions, 0 deletions
diff --git a/app/src/main/java/app/passwordstore/data/crypto/GPGPassphraseCache.kt b/app/src/main/java/app/passwordstore/data/crypto/GPGPassphraseCache.kt
new file mode 100644
index 00000000..d9e66f74
--- /dev/null
+++ b/app/src/main/java/app/passwordstore/data/crypto/GPGPassphraseCache.kt
@@ -0,0 +1,67 @@
+package app.passwordstore.data.crypto
+
+import android.content.Context
+import androidx.core.content.edit
+import androidx.security.crypto.EncryptedSharedPreferences
+import androidx.security.crypto.MasterKey
+import app.passwordstore.crypto.GpgIdentifier
+import app.passwordstore.util.coroutines.DispatcherProvider
+import app.passwordstore.util.extensions.getString
+import javax.inject.Inject
+import kotlinx.coroutines.withContext
+
+/** Implements a rudimentary [EncryptedSharedPreferences]-backed cache for GPG passphrases. */
+@Suppress("Unused") // Soon
+class GPGPassphraseCache
+@Inject
+constructor(
+ private val dispatcherProvider: DispatcherProvider,
+) {
+
+ private suspend fun cachePassphrase(
+ context: Context,
+ identifier: GpgIdentifier,
+ passphrase: String,
+ ) {
+ withContext(dispatcherProvider.io()) {
+ getPreferences(context).edit { putString(identifier.toString(), passphrase) }
+ }
+ }
+
+ private suspend fun retrieveCachedPassphrase(
+ context: Context,
+ identifier: GpgIdentifier,
+ ): String? {
+ return withContext(dispatcherProvider.io()) {
+ getPreferences(context).getString(identifier.toString())
+ }
+ }
+
+ private suspend fun getPreferences(context: Context) =
+ withContext(dispatcherProvider.io()) {
+ EncryptedSharedPreferences.create(
+ context,
+ ANDROIDX_SECURITY_KEYSET_PREF_NAME,
+ getOrCreateWrappingMasterKey(context),
+ EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
+ EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
+ )
+ }
+
+ private suspend fun getOrCreateWrappingMasterKey(context: Context) =
+ withContext(dispatcherProvider.io()) {
+ MasterKey.Builder(context, "passphrase")
+ .setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
+ .setRequestStrongBoxBacked(true)
+ .setUserAuthenticationRequired(
+ /* authenticationRequired = */ true,
+ /* userAuthenticationValidityDurationSeconds = */ 60,
+ )
+ .build()
+ }
+
+ private companion object {
+
+ private const val ANDROIDX_SECURITY_KEYSET_PREF_NAME = "androidx_passphrase_keyset_prefs"
+ }
+}