mirror of
https://github.com/bitwarden/android.git
synced 2024-11-21 17:05:44 +03:00
Add EncryptedSharedPreferences and BaseEncryptedDiskSource (#543)
This commit is contained in:
parent
14d686af76
commit
2d4427a7cf
15 changed files with 148 additions and 8 deletions
|
@ -72,6 +72,11 @@ The following is a list of all third-party dependencies included as part of the
|
|||
- Purpose: Display and capture images for barcode scanning.
|
||||
- License: Apache 2.0
|
||||
|
||||
- **AndroidX Security**
|
||||
- https://developer.android.com/jetpack/androidx/releases/security
|
||||
- Purpose: Safely manage keys and encrypt files and sharedpreferences.
|
||||
- License: Apache 2.0
|
||||
|
||||
- **Core SplashScreen**
|
||||
- https://developer.android.com/jetpack/androidx/releases/core
|
||||
- Purpose: Backwards compatible SplashScreen API implementation.
|
||||
|
|
|
@ -121,6 +121,7 @@ dependencies {
|
|||
ksp(libs.androidx.room.compiler)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.security.crypto)
|
||||
implementation(libs.androidx.splashscreen)
|
||||
implementation(libs.bitwarden.sdk)
|
||||
implementation(libs.bumptech.glide)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
|
||||
<application
|
||||
android:name=".BitwardenApplication"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
|
|
@ -2,8 +2,8 @@ package com.x8bit.bitwarden.data.auth.datasource.disk
|
|||
|
||||
import android.content.SharedPreferences
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.model.UserStateJson
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseDiskSource.Companion.BASE_KEY
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.BaseEncryptedDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.repository.util.bufferedMutableSharedFlow
|
||||
import com.x8bit.bitwarden.data.vault.datasource.network.model.SyncResponseJson
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
@ -26,9 +26,13 @@ private const val ORGANIZATION_KEYS_KEY = "$BASE_KEY:encOrgKeys"
|
|||
*/
|
||||
@Suppress("TooManyFunctions")
|
||||
class AuthDiskSourceImpl(
|
||||
encryptedSharedPreferences: SharedPreferences,
|
||||
sharedPreferences: SharedPreferences,
|
||||
private val json: Json,
|
||||
) : BaseDiskSource(sharedPreferences = sharedPreferences),
|
||||
) : BaseEncryptedDiskSource(
|
||||
encryptedSharedPreferences = encryptedSharedPreferences,
|
||||
sharedPreferences = sharedPreferences,
|
||||
),
|
||||
AuthDiskSource {
|
||||
private val mutableOrganizationsFlowMap =
|
||||
mutableMapOf<String, MutableSharedFlow<List<SyncResponseJson.Profile.Organization>?>>()
|
||||
|
|
|
@ -3,6 +3,8 @@ package com.x8bit.bitwarden.data.auth.datasource.disk.di
|
|||
import android.content.SharedPreferences
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
|
||||
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSourceImpl
|
||||
import com.x8bit.bitwarden.data.platform.datasource.di.EncryptedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.di.UnencryptedPreferences
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -20,10 +22,12 @@ object AuthDiskModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideAuthDiskSource(
|
||||
sharedPreferences: SharedPreferences,
|
||||
@EncryptedPreferences encryptedSharedPreferences: SharedPreferences,
|
||||
@UnencryptedPreferences sharedPreferences: SharedPreferences,
|
||||
json: Json,
|
||||
): AuthDiskSource =
|
||||
AuthDiskSourceImpl(
|
||||
encryptedSharedPreferences = encryptedSharedPreferences,
|
||||
sharedPreferences = sharedPreferences,
|
||||
json = json,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.di
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import javax.inject.Qualifier
|
||||
|
||||
/**
|
||||
* Used to denote an instance of [SharedPreferences] that encrypts its data.
|
||||
*/
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class EncryptedPreferences
|
|
@ -3,6 +3,8 @@ package com.x8bit.bitwarden.data.platform.datasource.di
|
|||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
import androidx.security.crypto.MasterKey
|
||||
import dagger.Module
|
||||
import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
|
@ -18,11 +20,29 @@ object PreferenceModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideDefaultSharedPreferences(
|
||||
@UnencryptedPreferences
|
||||
fun provideUnencryptedSharedPreferences(
|
||||
application: Application,
|
||||
): SharedPreferences =
|
||||
application.getSharedPreferences(
|
||||
"${application.packageName}_preferences",
|
||||
Context.MODE_PRIVATE,
|
||||
)
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
@EncryptedPreferences
|
||||
fun provideEncryptedSharedPreferences(
|
||||
application: Application,
|
||||
): SharedPreferences =
|
||||
EncryptedSharedPreferences
|
||||
.create(
|
||||
application,
|
||||
"${application.packageName}_encrypted_preferences",
|
||||
MasterKey.Builder(application)
|
||||
.setKeyScheme(MasterKey.KeyScheme.AES256_GCM)
|
||||
.build(),
|
||||
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
|
||||
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.di
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import javax.inject.Qualifier
|
||||
|
||||
/**
|
||||
* Used to denote an instance of [SharedPreferences] that does not encrypt its data.
|
||||
*/
|
||||
@Qualifier
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class UnencryptedPreferences
|
|
@ -0,0 +1,31 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.content.edit
|
||||
import androidx.security.crypto.EncryptedSharedPreferences
|
||||
|
||||
/**
|
||||
* Base class for simplifying interactions with [SharedPreferences] and
|
||||
* [EncryptedSharedPreferences].
|
||||
*/
|
||||
@Suppress("UnnecessaryAbstractClass")
|
||||
abstract class BaseEncryptedDiskSource(
|
||||
sharedPreferences: SharedPreferences,
|
||||
private val encryptedSharedPreferences: SharedPreferences,
|
||||
) : BaseDiskSource(
|
||||
sharedPreferences = sharedPreferences,
|
||||
) {
|
||||
protected fun getEncryptedString(
|
||||
key: String,
|
||||
default: String? = null,
|
||||
): String? = encryptedSharedPreferences.getString(key, default)
|
||||
|
||||
protected fun putEncryptedString(
|
||||
key: String,
|
||||
value: String?,
|
||||
): Unit = encryptedSharedPreferences.edit { putString(key, value) }
|
||||
|
||||
companion object {
|
||||
const val ENCRYPTED_BASE_KEY: String = "bwSecureStorage"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package com.x8bit.bitwarden.data.platform.datasource.disk.di
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.di.UnencryptedPreferences
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSource
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.EnvironmentDiskSourceImpl
|
||||
import com.x8bit.bitwarden.data.platform.datasource.disk.SettingsDiskSource
|
||||
|
@ -22,7 +23,7 @@ object PlatformDiskModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideEnvironmentDiskSource(
|
||||
sharedPreferences: SharedPreferences,
|
||||
@UnencryptedPreferences sharedPreferences: SharedPreferences,
|
||||
json: Json,
|
||||
): EnvironmentDiskSource =
|
||||
EnvironmentDiskSourceImpl(
|
||||
|
@ -33,7 +34,7 @@ object PlatformDiskModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideSettingsDiskSource(
|
||||
sharedPreferences: SharedPreferences,
|
||||
@UnencryptedPreferences sharedPreferences: SharedPreferences,
|
||||
): SettingsDiskSource =
|
||||
SettingsDiskSourceImpl(
|
||||
sharedPreferences = sharedPreferences,
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.x8bit.bitwarden.data.tools.generator.datasource.disk.di
|
|||
import android.app.Application
|
||||
import android.content.SharedPreferences
|
||||
import androidx.room.Room
|
||||
import com.x8bit.bitwarden.data.platform.datasource.di.UnencryptedPreferences
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSource
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.GeneratorDiskSourceImpl
|
||||
import com.x8bit.bitwarden.data.tools.generator.datasource.disk.PasswordHistoryDiskSource
|
||||
|
@ -26,7 +27,7 @@ object GeneratorDiskModule {
|
|||
@Provides
|
||||
@Singleton
|
||||
fun provideGeneratorDiskSource(
|
||||
sharedPreferences: SharedPreferences,
|
||||
@UnencryptedPreferences sharedPreferences: SharedPreferences,
|
||||
json: Json,
|
||||
): GeneratorDiskSource =
|
||||
GeneratorDiskSourceImpl(
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<full-backup-content>
|
||||
<exclude
|
||||
domain="sharedpref"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="file"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="database"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="external"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="root"
|
||||
path="." />
|
||||
</full-backup-content>
|
||||
|
|
|
@ -1,5 +1,37 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<exclude
|
||||
domain="sharedpref"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="file"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="database"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="external"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="root"
|
||||
path="." />
|
||||
</cloud-backup>
|
||||
<device-transfer>
|
||||
<exclude
|
||||
domain="sharedpref"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="file"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="database"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="external"
|
||||
path="." />
|
||||
<exclude
|
||||
domain="root"
|
||||
path="." />
|
||||
</device-transfer>
|
||||
</data-extraction-rules>
|
||||
|
|
|
@ -20,11 +20,13 @@ import org.junit.jupiter.api.Assertions.assertNull
|
|||
import org.junit.jupiter.api.Test
|
||||
|
||||
class AuthDiskSourceTest {
|
||||
private val fakeEncryptedSharedPreferences = FakeSharedPreferences()
|
||||
private val fakeSharedPreferences = FakeSharedPreferences()
|
||||
|
||||
private val json = PlatformNetworkModule.providesJson()
|
||||
|
||||
private val authDiskSource = AuthDiskSourceImpl(
|
||||
encryptedSharedPreferences = fakeEncryptedSharedPreferences,
|
||||
sharedPreferences = fakeSharedPreferences,
|
||||
json = json,
|
||||
)
|
||||
|
|
|
@ -21,6 +21,7 @@ androidxHiltNavigationCompose = "1.1.0"
|
|||
androidxLifecycle = "2.6.2"
|
||||
androidxNavigation = "2.7.6"
|
||||
androidxRoom = "2.6.1"
|
||||
androidXSecurityCrypto = "1.1.0-alpha06"
|
||||
androidxSplash = "1.1.0-alpha02"
|
||||
# Once the app and SDK reach a critical point of completeness we should begin fixing the version
|
||||
# here (BIT-311).
|
||||
|
@ -74,6 +75,7 @@ androidx-navigation-compose = { module = "androidx.navigation:navigation-compose
|
|||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "androidxRoom" }
|
||||
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "androidxRoom" }
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "androidxRoom" }
|
||||
androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "androidXSecurityCrypto" }
|
||||
androidx-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidxSplash" }
|
||||
bitwarden-sdk = { module = "com.bitwarden:sdk-android", version.ref = "bitwardenSdk" }
|
||||
bumptech-glide = { module = "com.github.bumptech.glide:compose", version.ref = "glide" }
|
||||
|
|
Loading…
Reference in a new issue