Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ private const val BIOMETRICS_UNLOCK_KEY = "userKeyBiometricUnlock"
private const val USER_AUTO_UNLOCK_KEY_KEY = "userKeyAutoUnlock"
private const val DEVICE_KEY_KEY = "deviceKey"
private const val PENDING_ADMIN_AUTH_REQUEST_KEY = "pendingAdminAuthRequest"
private const val ACCOUNT_CRYPTOGRAPHIC_STATE_KEY = "accountCryptographicState"

// These keys should not be encrypted
private const val UNIQUE_APP_ID_KEY = "appId"
Expand All @@ -57,19 +58,20 @@ private const val ONBOARDING_STATUS_KEY = "onboardingStatus"
private const val SHOW_IMPORT_LOGINS_KEY = "showImportLogins"
private const val LAST_LOCK_TIMESTAMP = "lastLockTimestamp"
private const val PROFILE_ACCOUNT_KEYS_KEY = "profileAccountKeys"
private const val ACCOUNT_CRYPTOGRAPHIC_STATE_KEY = "accountCryptographicState"

/**
* Primary implementation of [AuthDiskSource].
*/
@Suppress("TooManyFunctions")
class AuthDiskSourceImpl(
encryptedSharedPreferences: SharedPreferences,
keystoreEncryptedPreferences: SharedPreferences,
sharedPreferences: SharedPreferences,
legacySecureStorageMigrator: LegacySecureStorageMigrator,
private val json: Json,
) : BaseEncryptedDiskSource(
encryptedSharedPreferences = encryptedSharedPreferences,
keystoreEncryptedPreferences = keystoreEncryptedPreferences,
sharedPreferences = sharedPreferences,
),
AuthDiskSource {
Expand Down Expand Up @@ -125,6 +127,9 @@ class AuthDiskSourceImpl(
// We must migrate the Private Key and Account Keys to use the Account Cryptographic state
// from now on.
migrateAccountKeys()

// Migrate to the Keystore Encrypted SharedPreferences.
migrateToKeystoreEncryption()
}

override var authenticatorSyncSymmetricKey: ByteArray?
Expand Down Expand Up @@ -270,10 +275,7 @@ class AuthDiskSourceImpl(
}

override fun getUserAutoUnlockKey(userId: String): String? =
getEncryptedString(
key = USER_AUTO_UNLOCK_KEY_KEY.appendIdentifier(userId),
default = null,
)
getEncryptedString(key = USER_AUTO_UNLOCK_KEY_KEY.appendIdentifier(userId))

override fun storeUserAutoUnlockKey(
userId: String,
Expand Down Expand Up @@ -688,4 +690,16 @@ class AuthDiskSourceImpl(
}
}
}

private fun migrateToKeystoreEncryption() {
migrateKeyByPrefix(keyPrefix = AUTHENTICATOR_SYNC_SYMMETRIC_KEY)
migrateKeyByPrefix(keyPrefix = AUTHENTICATOR_SYNC_UNLOCK_KEY)
migrateKeyByPrefix(keyPrefix = ACCOUNT_CRYPTOGRAPHIC_STATE_KEY)
migrateKeyByPrefix(keyPrefix = USER_AUTO_UNLOCK_KEY_KEY)
migrateKeyByPrefix(keyPrefix = DEVICE_KEY_KEY)
migrateKeyByPrefix(keyPrefix = PENDING_ADMIN_AUTH_REQUEST_KEY)
migrateKeyByPrefix(keyPrefix = BIOMETRICS_INIT_VECTOR_KEY)
migrateKeyByPrefix(keyPrefix = BIOMETRICS_UNLOCK_KEY)
migrateKeyByPrefix(keyPrefix = ACCOUNT_TOKENS_KEY)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.x8bit.bitwarden.data.auth.datasource.disk.di

import android.content.SharedPreferences
import com.bitwarden.data.datasource.disk.di.EncryptedPreferences
import com.bitwarden.data.datasource.disk.di.KeystoreEncryptedPreferences
import com.bitwarden.data.datasource.disk.di.UnencryptedPreferences
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSource
import com.x8bit.bitwarden.data.auth.datasource.disk.AuthDiskSourceImpl
Expand All @@ -24,12 +25,14 @@ object AuthDiskModule {
@Singleton
fun provideAuthDiskSource(
@EncryptedPreferences encryptedSharedPreferences: SharedPreferences,
@KeystoreEncryptedPreferences keystoreEncryptedPreferences: SharedPreferences,
@UnencryptedPreferences sharedPreferences: SharedPreferences,
legacySecureStorageMigrator: LegacySecureStorageMigrator,
json: Json,
): AuthDiskSource =
AuthDiskSourceImpl(
encryptedSharedPreferences = encryptedSharedPreferences,
keystoreEncryptedPreferences = keystoreEncryptedPreferences,
sharedPreferences = sharedPreferences,
legacySecureStorageMigrator = legacySecureStorageMigrator,
json = json,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,21 @@ private const val ENCRYPTED_PREFIX = "bwSecureStorage:$CONFIG_PREFIX"
*/
class CookieDiskSourceImpl(
sharedPreferences: SharedPreferences,
keystoreEncryptedPreferences: SharedPreferences,
private val encryptedSharedPreferences: SharedPreferences,
private val json: Json,
) : CookieDiskSource,
BaseEncryptedDiskSource(
sharedPreferences = sharedPreferences,
keystoreEncryptedPreferences = keystoreEncryptedPreferences,
encryptedSharedPreferences = encryptedSharedPreferences,
) {

init {
// Migrate to the Keystore Encrypted SharedPreferences.
migrateToKeystoreEncryption()
}

override fun getCookieConfig(hostname: String): CookieConfigurationData? {
val key = CONFIG_PREFIX.appendIdentifier(hostname)
return getEncryptedString(key)
Expand All @@ -45,4 +52,8 @@ class CookieDiskSourceImpl(
keysToRemove.forEach { key -> remove(key) }
}
}

private fun migrateToKeystoreEncryption() {
migrateKeyByPrefix(keyPrefix = CONFIG_PREFIX)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.room.Room
import com.bitwarden.core.data.manager.dispatcher.DispatcherManager
import com.bitwarden.data.datasource.disk.FlightRecorderDiskSource
import com.bitwarden.data.datasource.disk.di.EncryptedPreferences
import com.bitwarden.data.datasource.disk.di.KeystoreEncryptedPreferences
import com.bitwarden.data.datasource.disk.di.UnencryptedPreferences
import com.x8bit.bitwarden.data.platform.datasource.disk.CookieDiskSource
import com.x8bit.bitwarden.data.platform.datasource.disk.CookieDiskSourceImpl
Expand Down Expand Up @@ -162,10 +163,12 @@ object PlatformDiskModule {
@Singleton
fun provideCookieDiskSource(
@UnencryptedPreferences sharedPreferences: SharedPreferences,
@KeystoreEncryptedPreferences keystoreEncryptedPreferences: SharedPreferences,
@EncryptedPreferences encryptedSharedPreferences: SharedPreferences,
json: Json,
): CookieDiskSource = CookieDiskSourceImpl(
sharedPreferences = sharedPreferences,
keystoreEncryptedPreferences = keystoreEncryptedPreferences,
encryptedSharedPreferences = encryptedSharedPreferences,
json = json,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,21 @@ private const val UNIQUE_APP_ID_KEY = "appId"
*/
class AuthDiskSourceImpl(
encryptedSharedPreferences: SharedPreferences,
keystoreEncryptedPreferences: SharedPreferences,
sharedPreferences: SharedPreferences,
) : BaseEncryptedDiskSource(
encryptedSharedPreferences = encryptedSharedPreferences,
keystoreEncryptedPreferences = keystoreEncryptedPreferences,
sharedPreferences = sharedPreferences,
),
AuthDiskSource {
private val mutableUserBiometricUnlockKeyFlow = bufferedMutableSharedFlow<String?>(replay = 1)

init {
// Migrate to the Keystore Encrypted SharedPreferences.
migrateToKeystoreEncryption()
}

override val uniqueAppId: String
get() = getString(key = UNIQUE_APP_ID_KEY) ?: generateAndStoreUniqueAppId()

Expand Down Expand Up @@ -86,4 +93,10 @@ class AuthDiskSourceImpl(
.also {
putString(key = UNIQUE_APP_ID_KEY, value = it)
}

private fun migrateToKeystoreEncryption() {
migrateKeyByPrefix(keyPrefix = BIOMETRICS_UNLOCK_KEY)
migrateKeyByPrefix(keyPrefix = BIOMETRICS_INIT_VECTOR_KEY)
migrateKeyByPrefix(keyPrefix = AUTHENTICATOR_SYNC_SYMMETRIC_KEY)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import android.content.SharedPreferences
import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSource
import com.bitwarden.authenticator.data.auth.datasource.disk.AuthDiskSourceImpl
import com.bitwarden.data.datasource.disk.di.EncryptedPreferences
import com.bitwarden.data.datasource.disk.di.KeystoreEncryptedPreferences
import com.bitwarden.data.datasource.disk.di.UnencryptedPreferences
import dagger.Module
import dagger.Provides
Expand All @@ -22,10 +23,12 @@ object AuthDiskModule {
@Singleton
fun provideAuthDiskSource(
@EncryptedPreferences encryptedSharedPreferences: SharedPreferences,
@KeystoreEncryptedPreferences keystoreEncryptedPreferences: SharedPreferences,
@UnencryptedPreferences sharedPreferences: SharedPreferences,
): AuthDiskSource =
AuthDiskSourceImpl(
encryptedSharedPreferences = encryptedSharedPreferences,
keystoreEncryptedPreferences = keystoreEncryptedPreferences,
sharedPreferences = sharedPreferences,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,33 @@ import androidx.security.crypto.EncryptedSharedPreferences
abstract class BaseEncryptedDiskSource(
sharedPreferences: SharedPreferences,
private val encryptedSharedPreferences: SharedPreferences,
) : BaseDiskSource(
sharedPreferences = sharedPreferences,
) {
private val keystoreEncryptedPreferences: SharedPreferences,
) : BaseDiskSource(sharedPreferences = sharedPreferences) {
protected fun getEncryptedString(
key: String,
default: String? = null,
): String? = encryptedSharedPreferences.getString(key.withBase(), default)
): String? = keystoreEncryptedPreferences.getString(key.withBase(), null)

protected fun putEncryptedString(
key: String,
value: String?,
): Unit = encryptedSharedPreferences.edit { putString(key.withBase(), value) }
) {
keystoreEncryptedPreferences.edit { putString(key.withBase(), value) }
}

protected fun migrateKeyByPrefix(keyPrefix: String) {
encryptedSharedPreferences
.all
.keys
.filter { it.startsWith(prefix = keyPrefix.withBase()) }
.forEach { key ->
// Move the value to the new file.
encryptedSharedPreferences.getString(key, null)?.let { value ->
keystoreEncryptedPreferences.edit(commit = true) { putString(key, value) }
}
// Then ensure the original value is deleted.
encryptedSharedPreferences.edit(commit = true) { remove(key) }
}
}
}

/**
Expand Down
Loading
Loading