mirror of
https://github.com/anyproto/anytype-kotlin.git
synced 2025-06-08 05:47:05 +09:00
Feature | Encrypt sensitive data within EncryptedSharedPreferences (#1197)
This commit is contained in:
parent
826f001dd7
commit
5bcc165fcd
6 changed files with 74 additions and 15 deletions
|
@ -141,6 +141,8 @@ dependencies {
|
|||
implementation applicationDependencies.photoView
|
||||
implementation applicationDependencies.zxing
|
||||
|
||||
implementation applicationDependencies.androidxSecurityCrypto
|
||||
|
||||
//Unit/Integration tests dependencies
|
||||
testImplementation unitTestDependencies.junit
|
||||
testImplementation unitTestDependencies.robolectric
|
||||
|
|
|
@ -2,6 +2,8 @@ package com.anytypeio.anytype.di.main
|
|||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKeys
|
||||
import com.anytypeio.anytype.data.auth.repo.*
|
||||
import com.anytypeio.anytype.data.auth.repo.block.BlockDataRepository
|
||||
import com.anytypeio.anytype.data.auth.repo.block.BlockDataStoreFactory
|
||||
|
@ -27,6 +29,7 @@ import com.anytypeio.anytype.persistence.repo.DefaultAuthCache
|
|||
import com.anytypeio.anytype.persistence.repo.DefaultDebugSettingsCache
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import javax.inject.Named
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
|
@ -80,11 +83,13 @@ object DataModule {
|
|||
@Singleton
|
||||
fun provideAuthCache(
|
||||
db: AnytypeDatabase,
|
||||
prefs: SharedPreferences
|
||||
@Named("default") defaultPrefs: SharedPreferences,
|
||||
@Named("encrypted") encryptedPrefs: SharedPreferences
|
||||
): AuthCache {
|
||||
return DefaultAuthCache(
|
||||
db = db,
|
||||
prefs = prefs
|
||||
defaultPrefs = defaultPrefs,
|
||||
encryptedPrefs = encryptedPrefs
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -92,7 +97,7 @@ object DataModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideDebugSettingsCache(
|
||||
prefs: SharedPreferences
|
||||
@Named("default") prefs: SharedPreferences,
|
||||
): DebugSettingsCache {
|
||||
return DefaultDebugSettingsCache(
|
||||
prefs = prefs
|
||||
|
@ -120,10 +125,25 @@ object DataModule {
|
|||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("default")
|
||||
fun provideSharedPreferences(context: Context): SharedPreferences {
|
||||
return context.getSharedPreferences("prefs", Context.MODE_PRIVATE)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
@Named("encrypted")
|
||||
fun provideEncryptedSharedPreferences(
|
||||
context: Context
|
||||
): SharedPreferences = EncryptedSharedPreferences.create(
|
||||
"encrypted_prefs",
|
||||
MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
|
||||
context,
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
@Provides
|
||||
@Singleton
|
||||
|
|
|
@ -15,6 +15,7 @@ ext {
|
|||
androidx_core_ktx_version = '1.5.0-alpha02'
|
||||
androidx_test_core_version = '1.2.0'
|
||||
androidx_core_testing_version = '2.1.0'
|
||||
androidx_security_crypto_version = '1.0.0-rc03'
|
||||
appcompat_version = '1.2.0'
|
||||
constraintLayout_version = '2.0.4'
|
||||
recyclerview_version = '1.2.0-alpha06'
|
||||
|
@ -130,7 +131,8 @@ ext {
|
|||
kotlinxSerializationJson: "org.jetbrains.kotlinx:kotlinx-serialization-json:$kotlinx_serialization_json_version",
|
||||
crashlytics: "com.google.firebase:firebase-crashlytics:$crashlytics_version",
|
||||
firebaseCore: "com.google.firebase:firebase-core:$firebase_core_version",
|
||||
shimmerLayout: "com.facebook.shimmer:shimmer:$shimmer_layout_version"
|
||||
shimmerLayout: "com.facebook.shimmer:shimmer:$shimmer_layout_version",
|
||||
androidxSecurityCrypto: "androidx.security:security-crypto:$androidx_security_crypto_version"
|
||||
]
|
||||
|
||||
unitTesting = [
|
||||
|
|
|
@ -23,7 +23,6 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation project(':data')
|
||||
|
||||
def applicationDependencies = rootProject.ext.mainApplication
|
||||
|
|
|
@ -25,5 +25,4 @@ abstract class AnytypeDatabase : RoomDatabase() {
|
|||
Config.DATABASE_NAME
|
||||
).build()
|
||||
}
|
||||
|
||||
}
|
|
@ -9,7 +9,8 @@ import com.anytypeio.anytype.persistence.mapper.toTable
|
|||
|
||||
class DefaultAuthCache(
|
||||
private val db: AnytypeDatabase,
|
||||
private val prefs: SharedPreferences
|
||||
private val defaultPrefs: SharedPreferences,
|
||||
private val encryptedPrefs: SharedPreferences
|
||||
) : AuthCache {
|
||||
|
||||
override suspend fun saveAccount(account: AccountEntity) {
|
||||
|
@ -25,30 +26,66 @@ class DefaultAuthCache(
|
|||
?: throw IllegalStateException("Account with the following id not found: $id")
|
||||
}
|
||||
|
||||
/**
|
||||
* N.B. Migrating sensitive data from default to encrypted prefs.
|
||||
*/
|
||||
override suspend fun getCurrentAccountId(): String {
|
||||
val id: String? = prefs.getString(CURRENT_ACCOUNT_ID_KEY, null)
|
||||
return id ?: throw IllegalStateException("Current account not set")
|
||||
val nonEncryptedId = defaultPrefs.getString(CURRENT_ACCOUNT_ID_KEY, null)
|
||||
return if (nonEncryptedId != null) {
|
||||
encryptedPrefs.edit().putString(CURRENT_ACCOUNT_ID_KEY, nonEncryptedId).apply()
|
||||
defaultPrefs.edit().remove(CURRENT_ACCOUNT_ID_KEY).apply()
|
||||
nonEncryptedId
|
||||
} else {
|
||||
val encryptedId = encryptedPrefs.getString(CURRENT_ACCOUNT_ID_KEY, null)
|
||||
encryptedId ?: throw IllegalStateException("Current account not set")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* N.B. Migrating sensitive data from default to encrypted prefs.
|
||||
*/
|
||||
override suspend fun saveMnemonic(mnemonic: String) {
|
||||
prefs.edit().putString(MNEMONIC_KEY, mnemonic).apply()
|
||||
defaultPrefs.edit().remove(MNEMONIC_KEY).apply()
|
||||
encryptedPrefs.edit().putString(MNEMONIC_KEY, mnemonic).apply()
|
||||
}
|
||||
|
||||
/**
|
||||
* N.B. Migrating sensitive data from default to encrypted prefs.
|
||||
*/
|
||||
override suspend fun getMnemonic(): String {
|
||||
return prefs.getString(MNEMONIC_KEY, null)
|
||||
?: throw IllegalStateException("Mnemonic is missing.")
|
||||
val nonEncryptedMnemonic = defaultPrefs.getString(MNEMONIC_KEY, null)
|
||||
return if (nonEncryptedMnemonic != null) {
|
||||
encryptedPrefs.edit().putString(MNEMONIC_KEY, nonEncryptedMnemonic).apply()
|
||||
defaultPrefs.edit().remove(MNEMONIC_KEY).apply()
|
||||
nonEncryptedMnemonic
|
||||
} else {
|
||||
val encryptedMnemonic = encryptedPrefs.getString(MNEMONIC_KEY, null)
|
||||
encryptedMnemonic ?: throw IllegalStateException("Mnemonic is missing")
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun logout() {
|
||||
db.accountDao().clear()
|
||||
prefs.edit().putString(MNEMONIC_KEY, null).apply()
|
||||
defaultPrefs
|
||||
.edit()
|
||||
.remove(MNEMONIC_KEY)
|
||||
.remove(CURRENT_ACCOUNT_ID_KEY)
|
||||
.apply()
|
||||
encryptedPrefs
|
||||
.edit()
|
||||
.remove(MNEMONIC_KEY)
|
||||
.remove(CURRENT_ACCOUNT_ID_KEY)
|
||||
.apply()
|
||||
}
|
||||
|
||||
override suspend fun getAccounts() = db.accountDao().getAccounts().map { it.toEntity() }
|
||||
|
||||
|
||||
/**
|
||||
* N.B. Migrating sensitive data from default to encrypted prefs.
|
||||
*/
|
||||
override suspend fun setCurrentAccount(id: String) {
|
||||
prefs.edit().putString(CURRENT_ACCOUNT_ID_KEY, id).apply()
|
||||
defaultPrefs.edit().remove(CURRENT_ACCOUNT_ID_KEY).apply()
|
||||
encryptedPrefs.edit().putString(CURRENT_ACCOUNT_ID_KEY, id).apply()
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue