BIT-2437: Add mitigation logic for bad encryption key (#3426)

This commit is contained in:
David Perez 2024-08-07 11:07:17 -05:00 committed by GitHub
parent d8471b41ca
commit 782b474e54
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 93 additions and 11 deletions

View file

@ -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),
)
}

View file

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

View file

@ -3,16 +3,28 @@
<exclude
domain="sharedpref"
path="." />
<exclude
domain="device_sharedpref"
path="." />
<exclude
domain="file"
path="." />
<exclude
domain="device_file"
path="." />
<exclude
domain="database"
path="." />
<exclude
domain="device_database"
path="." />
<exclude
domain="external"
path="." />
<exclude
domain="root"
path="." />
<exclude
domain="device_root"
path="." />
</full-backup-content>

View file

@ -4,34 +4,58 @@
<exclude
domain="sharedpref"
path="." />
<exclude
domain="device_sharedpref"
path="." />
<exclude
domain="file"
path="." />
<exclude
domain="device_file"
path="." />
<exclude
domain="database"
path="." />
<exclude
domain="device_database"
path="." />
<exclude
domain="external"
path="." />
<exclude
domain="root"
path="." />
<exclude
domain="device_root"
path="." />
</cloud-backup>
<device-transfer>
<exclude
domain="sharedpref"
path="." />
<exclude
domain="device_sharedpref"
path="." />
<exclude
domain="file"
path="." />
<exclude
domain="device_file"
path="." />
<exclude
domain="database"
path="." />
<exclude
domain="device_database"
path="." />
<exclude
domain="external"
path="." />
<exclude
domain="root"
path="." />
<exclude
domain="device_root"
path="." />
</device-transfer>
</data-extraction-rules>