feat: kick off a very basic passphrase cache
This commit is contained in:
parent
1e68e97b25
commit
f9730cae58
1 changed files with 67 additions and 0 deletions
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue