From 782b474e54e082a841dc79185dde6d71b016b465 Mon Sep 17 00:00:00 2001 From: David Perez Date: Wed, 7 Aug 2024 11:07:17 -0500 Subject: [PATCH] BIT-2437: Add mitigation logic for bad encryption key (#3426) --- .../auth/repository/AuthRepositoryImpl.kt | 3 +- .../datasource/di/PreferenceModule.kt | 65 ++++++++++++++++--- app/src/main/res/xml/backup_rules.xml | 12 ++++ .../main/res/xml/data_extraction_rules.xml | 24 +++++++ 4 files changed, 93 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt index 3dc4161c2..18c6eea4c 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/auth/repository/AuthRepositoryImpl.kt @@ -1626,7 +1626,8 @@ class AuthRepositoryImpl( // The value for the organization keys here will typically be null. We can separately // unlock the vault for organization data after receiving the sync response if this // data is currently absent. These keys may be present during certain multi-phase login - // processes. + // processes or if we needed to delete the user's token due to an encrypted data + // corruption issue and they are forced to log back in. organizationKeys = authDiskSource.getOrganizationKeys(userId = userId), ) } diff --git a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/di/PreferenceModule.kt b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/di/PreferenceModule.kt index cd69445f5..5b82b60ac 100644 --- a/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/di/PreferenceModule.kt +++ b/app/src/main/java/com/x8bit/bitwarden/data/platform/datasource/di/PreferenceModule.kt @@ -9,6 +9,8 @@ import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.components.SingletonComponent +import java.security.GeneralSecurityException +import java.security.KeyStore import javax.inject.Singleton /** @@ -35,14 +37,57 @@ object PreferenceModule { 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, - ) + @Suppress("TooGenericExceptionCaught") + try { + getEncryptedSharedPreferences(application = application) + } catch (e: GeneralSecurityException) { + // Handle when a bad master key or key-set has been attempted + destroyEncryptedSharedPreferencesAndRebuild(application = application) + } catch (e: RuntimeException) { + // Handle KeystoreExceptions that get wrapped up in a RuntimeException + destroyEncryptedSharedPreferencesAndRebuild(application = application) + } + + /** + * Completely destroys the keystore master key and encrypted shared preferences file. This will + * cause all users to be logged out since the access and refresh tokens will be removed. + * + * This is not desirable and should only be called if we have completely failed to access our + * encrypted shared preferences instance. + */ + private fun destroyEncryptedSharedPreferencesAndRebuild( + application: Application, + ): SharedPreferences { + // Delete the master key + KeyStore.getInstance(KeyStore.getDefaultType()).run { + load(null) + deleteEntry(MasterKey.DEFAULT_MASTER_KEY_ALIAS) + } + // Deletes the encrypted shared preferences file + application.deleteSharedPreferences(application.encryptedSharedPreferencesName) + // Attempts to create the encrypted shared preferences instance + return getEncryptedSharedPreferences(application = application) + } + + /** + * Helper method to get the app's encrypted shared preferences instance. + */ + private fun getEncryptedSharedPreferences( + application: Application, + ): SharedPreferences = + EncryptedSharedPreferences.create( + application, + application.encryptedSharedPreferencesName, + MasterKey.Builder(application) + .setKeyScheme(MasterKey.KeyScheme.AES256_GCM) + .build(), + EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, + EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM, + ) + + /** + * Helper method to get the app's encrypted shared preferences name. + */ + private val Application.encryptedSharedPreferencesName: String + get() = "${packageName}_encrypted_preferences" } diff --git a/app/src/main/res/xml/backup_rules.xml b/app/src/main/res/xml/backup_rules.xml index cf7893b5b..8730b8a3e 100644 --- a/app/src/main/res/xml/backup_rules.xml +++ b/app/src/main/res/xml/backup_rules.xml @@ -3,16 +3,28 @@ + + + + diff --git a/app/src/main/res/xml/data_extraction_rules.xml b/app/src/main/res/xml/data_extraction_rules.xml index b8ddf7c0d..89b68c26f 100644 --- a/app/src/main/res/xml/data_extraction_rules.xml +++ b/app/src/main/res/xml/data_extraction_rules.xml @@ -4,34 +4,58 @@ + + + + + + + +