Daggerization of RealmKeysUtils

This commit is contained in:
Benoit Marty 2019-09-16 15:50:56 +02:00
parent 138a210a73
commit 1f127335bc
4 changed files with 42 additions and 34 deletions

View File

@ -23,7 +23,7 @@ import dagger.Provides
import im.vector.matrix.android.api.auth.Authenticator
import im.vector.matrix.android.internal.auth.db.AuthRealmModule
import im.vector.matrix.android.internal.auth.db.RealmSessionParamsStore
import im.vector.matrix.android.internal.database.configureEncryption
import im.vector.matrix.android.internal.database.RealmKeysUtils
import im.vector.matrix.android.internal.di.AuthDatabase
import io.realm.RealmConfiguration
import java.io.File
@ -36,13 +36,16 @@ internal abstract class AuthModule {
@JvmStatic
@Provides
@AuthDatabase
fun providesRealmConfiguration(context: Context): RealmConfiguration {
fun providesRealmConfiguration(context: Context, realmKeysUtils: RealmKeysUtils): RealmConfiguration {
val old = File(context.filesDir, "matrix-sdk-auth")
if (old.exists()) {
old.renameTo(File(context.filesDir, "matrix-sdk-auth.realm"))
}
return RealmConfiguration.Builder()
.configureEncryption("matrix-sdk-auth", context)
.apply {
realmKeysUtils.configureEncryption(this, "matrix-sdk-auth")
}
.name("matrix-sdk-auth.realm")
.modules(AuthRealmModule())
.deleteRealmIfMigrationNeeded()

View File

@ -30,7 +30,7 @@ import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStore
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreMigration
import im.vector.matrix.android.internal.crypto.store.db.RealmCryptoStoreModule
import im.vector.matrix.android.internal.crypto.tasks.*
import im.vector.matrix.android.internal.database.configureEncryption
import im.vector.matrix.android.internal.database.RealmKeysUtils
import im.vector.matrix.android.internal.di.CryptoDatabase
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.cache.ClearCacheTask
@ -50,12 +50,14 @@ internal abstract class CryptoModule {
@Provides
@CryptoDatabase
@SessionScope
fun providesRealmConfiguration(context: Context, credentials: Credentials): RealmConfiguration {
fun providesRealmConfiguration(context: Context, credentials: Credentials, realmKeysUtils: RealmKeysUtils): RealmConfiguration {
val userIDHash = credentials.userId.md5()
return RealmConfiguration.Builder()
.directory(File(context.filesDir, userIDHash))
.configureEncryption("crypto_module_$userIDHash", context)
.apply {
realmKeysUtils.configureEncryption(this, "crypto_module_$userIDHash")
}
.name("crypto_store.realm")
.modules(RealmCryptoStoreModule())
.schemaVersion(RealmCryptoStoreMigration.CRYPTO_STORE_SCHEMA_VERSION)

View File

@ -17,10 +17,12 @@ package im.vector.matrix.android.internal.database
import android.content.Context
import android.util.Base64
import im.vector.matrix.android.BuildConfig
import im.vector.matrix.android.api.util.SecretStoringUtils
import io.realm.RealmConfiguration
import timber.log.Timber
import java.security.SecureRandom
import javax.inject.Inject
/**
* On creation a random key is generated, this key is then encrypted using the system KeyStore.
@ -34,12 +36,12 @@ import java.security.SecureRandom
* then we generate a random secret key. The database key is encrypted with the secret key; The secret
* key is encrypted with the public RSA key and stored with the encrypted key in the shared pref
*/
private object RealmKeysUtils {
private const val ENCRYPTED_KEY_PREFIX = "REALM_ENCRYPTED_KEY"
internal class RealmKeysUtils @Inject constructor(private val context: Context) {
private val rng = SecureRandom()
private val sharedPreferences = context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE)
private fun generateKeyForRealm(): ByteArray {
val keyForRealm = ByteArray(RealmConfiguration.KEY_LENGTH)
rng.nextBytes(keyForRealm)
@ -49,8 +51,7 @@ private object RealmKeysUtils {
/**
* Check if there is already a key for this alias
*/
fun hasKeyForDatabase(alias: String, context: Context): Boolean {
val sharedPreferences = getSharedPreferences(context)
private fun hasKeyForDatabase(alias: String): Boolean {
return sharedPreferences.contains("${ENCRYPTED_KEY_PREFIX}_$alias")
}
@ -59,13 +60,12 @@ private object RealmKeysUtils {
* The random key is then encrypted by the keystore, and the encrypted key is stored
* in shared preferences.
*
* @return the generate key (can be passed to Realm Configuration)
* @return the generated key (can be passed to Realm Configuration)
*/
fun createAndSaveKeyForDatabase(alias: String, context: Context): ByteArray {
private fun createAndSaveKeyForDatabase(alias: String): ByteArray {
val key = generateKeyForRealm()
val encodedKey = Base64.encodeToString(key, Base64.NO_PADDING)
val toStore = SecretStoringUtils.securelyStoreString(encodedKey, alias, context)
val sharedPreferences = getSharedPreferences(context)
sharedPreferences
.edit()
.putString("${ENCRYPTED_KEY_PREFIX}_$alias", Base64.encodeToString(toStore!!, Base64.NO_PADDING))
@ -77,30 +77,31 @@ private object RealmKeysUtils {
* Retrieves the key for this database
* throws if something goes wrong
*/
fun extractKeyForDatabase(alias: String, context: Context): ByteArray {
val sharedPreferences = getSharedPreferences(context)
private fun extractKeyForDatabase(alias: String): ByteArray {
val encryptedB64 = sharedPreferences.getString("${ENCRYPTED_KEY_PREFIX}_$alias", null)
val encryptedKey = Base64.decode(encryptedB64, Base64.NO_PADDING)
val b64 = SecretStoringUtils.loadSecureSecret(encryptedKey, alias, context)
return Base64.decode(b64!!, Base64.NO_PADDING)
}
private fun getSharedPreferences(context: Context) =
context.getSharedPreferences("im.vector.matrix.android.keys", Context.MODE_PRIVATE)
}
fun RealmConfiguration.Builder.configureEncryption(alias: String, context: Context): RealmConfiguration.Builder {
if (RealmKeysUtils.hasKeyForDatabase(alias, context)) {
Timber.i("Found key for alias:$alias")
RealmKeysUtils.extractKeyForDatabase(alias, context).also {
this.encryptionKey(it)
fun configureEncryption(realmConfigurationBuilder: RealmConfiguration.Builder, alias: String) {
val key = if (hasKeyForDatabase(alias)) {
Timber.i("Found key for alias:$alias")
extractKeyForDatabase(alias)
} else {
Timber.i("Create key for DB alias:$alias")
createAndSaveKeyForDatabase(alias)
}
} else {
Timber.i("Create key for DB alias:$alias")
RealmKeysUtils.createAndSaveKeyForDatabase(alias, context).also {
this.encryptionKey(it)
if (BuildConfig.LOG_PRIVATE_DATA) {
val log = key.joinToString("") { "%02x".format(it) }
Timber.w("Database key for alias `$alias`: $log")
}
realmConfigurationBuilder.encryptionKey(key)
}
companion object {
private const val ENCRYPTED_KEY_PREFIX = "REALM_ENCRYPTED_KEY"
}
return this
}

View File

@ -28,7 +28,7 @@ import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.InitialSyncProgressService
import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.internal.database.LiveEntityObserver
import im.vector.matrix.android.internal.database.configureEncryption
import im.vector.matrix.android.internal.database.RealmKeysUtils
import im.vector.matrix.android.internal.database.model.SessionRealmModule
import im.vector.matrix.android.internal.di.Authenticated
import im.vector.matrix.android.internal.di.SessionDatabase
@ -70,14 +70,16 @@ internal abstract class SessionModule {
@Provides
@SessionDatabase
@SessionScope
fun providesRealmConfiguration(sessionParams: SessionParams, context: Context): RealmConfiguration {
fun providesRealmConfiguration(sessionParams: SessionParams, realmKeysUtils: RealmKeysUtils, context: Context): RealmConfiguration {
val childPath = sessionParams.credentials.userId.md5()
val directory = File(context.filesDir, childPath)
return RealmConfiguration.Builder()
.directory(directory)
.name("disk_store.realm")
.configureEncryption("session_db_$childPath", context)
.apply {
realmKeysUtils.configureEncryption(this, "session_db_$childPath")
}
.modules(SessionRealmModule())
.deleteRealmIfMigrationNeeded()
.build()