1
0
Fork 0
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:
Evgenii Kozlov 2020-12-21 11:24:54 +03:00 committed by GitHub
parent 826f001dd7
commit 5bcc165fcd
Signed by: github
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 74 additions and 15 deletions

View file

@ -141,6 +141,8 @@ dependencies {
implementation applicationDependencies.photoView
implementation applicationDependencies.zxing
implementation applicationDependencies.androidxSecurityCrypto
//Unit/Integration tests dependencies
testImplementation unitTestDependencies.junit
testImplementation unitTestDependencies.robolectric

View file

@ -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

View file

@ -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 = [

View file

@ -23,7 +23,6 @@ android {
}
dependencies {
implementation project(':data')
def applicationDependencies = rootProject.ext.mainApplication

View file

@ -25,5 +25,4 @@ abstract class AnytypeDatabase : RoomDatabase() {
Config.DATABASE_NAME
).build()
}
}

View file

@ -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 {