Remove dependency on libolm - WIP
This commit is contained in:
parent
6a8e978204
commit
5908cd54f0
|
@ -205,9 +205,6 @@ dependencies {
|
||||||
// Work
|
// Work
|
||||||
implementation libs.androidx.work
|
implementation libs.androidx.work
|
||||||
|
|
||||||
// olm lib is now hosted in MavenCentral
|
|
||||||
implementation 'org.matrix.android:olm-sdk:3.2.12'
|
|
||||||
|
|
||||||
// DI
|
// DI
|
||||||
implementation libs.dagger.dagger
|
implementation libs.dagger.dagger
|
||||||
kapt libs.dagger.daggerCompiler
|
kapt libs.dagger.daggerCompiler
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:a7acd69f37612bab0a1ab7f456656712d7ba19dbb679f81b97b58ef44e239f42
|
|
||||||
size 8523776
|
|
|
@ -1,3 +0,0 @@
|
||||||
version https://git-lfs.github.com/spec/v1
|
|
||||||
oid sha256:59b4957aa2f9cdc17b14ec8546e144537fac9dee050c6eb173f56fa8602c2736
|
|
||||||
size 2097152
|
|
|
@ -36,7 +36,6 @@ import org.matrix.android.sdk.internal.network.ApiInterceptor
|
||||||
import org.matrix.android.sdk.internal.network.UserAgentHolder
|
import org.matrix.android.sdk.internal.network.UserAgentHolder
|
||||||
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
||||||
import org.matrix.olm.OlmManager
|
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -49,7 +48,6 @@ internal class TestMatrix(context: Context, matrixConfiguration: MatrixConfigura
|
||||||
@Inject internal lateinit var rawService: RawService
|
@Inject internal lateinit var rawService: RawService
|
||||||
@Inject internal lateinit var userAgentHolder: UserAgentHolder
|
@Inject internal lateinit var userAgentHolder: UserAgentHolder
|
||||||
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
||||||
@Inject internal lateinit var olmManager: OlmManager
|
|
||||||
@Inject internal lateinit var sessionManager: SessionManager
|
@Inject internal lateinit var sessionManager: SessionManager
|
||||||
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
||||||
@Inject internal lateinit var apiInterceptor: ApiInterceptor
|
@Inject internal lateinit var apiInterceptor: ApiInterceptor
|
||||||
|
|
|
@ -20,7 +20,6 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import org.junit.Assert.assertArrayEquals
|
import org.junit.Assert.assertArrayEquals
|
||||||
import org.junit.Assert.assertEquals
|
import org.junit.Assert.assertEquals
|
||||||
import org.junit.Assert.assertTrue
|
import org.junit.Assert.assertTrue
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.FixMethodOrder
|
import org.junit.FixMethodOrder
|
||||||
import org.junit.Ignore
|
import org.junit.Ignore
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
|
@ -29,19 +28,12 @@ import org.junit.runners.MethodSorters
|
||||||
import org.matrix.android.sdk.InstrumentedTest
|
import org.matrix.android.sdk.InstrumentedTest
|
||||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||||
import org.matrix.android.sdk.common.assertByteArrayNotEqual
|
import org.matrix.android.sdk.common.assertByteArrayNotEqual
|
||||||
import org.matrix.olm.OlmManager
|
|
||||||
import org.matrix.olm.OlmPkDecryption
|
|
||||||
|
|
||||||
@Ignore("Ignored in order to speed up test run time")
|
@Ignore("Ignored in order to speed up test run time")
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@FixMethodOrder(MethodSorters.JVM)
|
@FixMethodOrder(MethodSorters.JVM)
|
||||||
class KeysBackupPasswordTest : InstrumentedTest {
|
class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
|
|
||||||
@Before
|
|
||||||
fun ensureLibLoaded() {
|
|
||||||
OlmManager()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check KeysBackupPassword utilities
|
* Check KeysBackupPassword utilities
|
||||||
*/
|
*/
|
||||||
|
@ -51,7 +43,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
|
|
||||||
assertEquals(32, generatePrivateKeyResult.salt.length)
|
assertEquals(32, generatePrivateKeyResult.salt.length)
|
||||||
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, generatePrivateKeyResult.privateKey.size)
|
||||||
|
|
||||||
// Reverse operation
|
// Reverse operation
|
||||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
||||||
|
@ -60,7 +52,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
generatePrivateKeyResult.iterations
|
generatePrivateKeyResult.iterations
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||||
assertArrayEquals(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
assertArrayEquals(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -101,7 +93,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
|
|
||||||
assertEquals(32, generatePrivateKeyResult.salt.length)
|
assertEquals(32, generatePrivateKeyResult.salt.length)
|
||||||
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, generatePrivateKeyResult.privateKey.size)
|
||||||
|
|
||||||
// Reverse operation, with bad password
|
// Reverse operation, with bad password
|
||||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
||||||
|
@ -110,7 +102,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
generatePrivateKeyResult.iterations
|
generatePrivateKeyResult.iterations
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||||
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +115,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
|
|
||||||
assertEquals(32, generatePrivateKeyResult.salt.length)
|
assertEquals(32, generatePrivateKeyResult.salt.length)
|
||||||
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, generatePrivateKeyResult.privateKey.size)
|
||||||
|
|
||||||
// Reverse operation, with bad iteration
|
// Reverse operation, with bad iteration
|
||||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
||||||
|
@ -132,7 +124,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
500_001
|
500_001
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||||
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +137,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
|
|
||||||
assertEquals(32, generatePrivateKeyResult.salt.length)
|
assertEquals(32, generatePrivateKeyResult.salt.length)
|
||||||
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, generatePrivateKeyResult.privateKey.size)
|
||||||
|
|
||||||
// Reverse operation, with bad iteration
|
// Reverse operation, with bad iteration
|
||||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
||||||
|
@ -154,7 +146,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
generatePrivateKeyResult.iterations
|
generatePrivateKeyResult.iterations
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||||
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +161,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
|
|
||||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(password, salt, iteration)
|
val retrievedPrivateKey = retrievePrivateKeyWithPassword(password, salt, iteration)
|
||||||
|
|
||||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||||
|
|
||||||
// Data from RiotWeb
|
// Data from RiotWeb
|
||||||
val privateKeyBytes = byteArrayOf(
|
val privateKeyBytes = byteArrayOf(
|
||||||
|
@ -187,5 +179,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
||||||
private const val BAD_PASSWORD = "passw0rd"
|
private const val BAD_PASSWORD = "passw0rd"
|
||||||
|
|
||||||
private const val BAD_SALT = "AA0lxhQ9aYgGfMsclVWPIAublg8h9Nlu"
|
private const val BAD_SALT = "AA0lxhQ9aYgGfMsclVWPIAublg8h9Nlu"
|
||||||
|
|
||||||
|
private const val EXPECTED_PRIVATE_KEY_LENGTH = 32
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,147 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.migration
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import io.mockk.spyk
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
import org.amshove.kluent.internal.assertEquals
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Assert.assertNotNull
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Ignore
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.matrix.android.sdk.TestBuildVersionSdkIntProvider
|
|
||||||
import org.matrix.android.sdk.api.securestorage.SecretStoringUtils
|
|
||||||
import org.matrix.android.sdk.internal.crypto.RustEncryptionConfiguration
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RustMigrationInfoProvider
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.database.RealmKeysUtils
|
|
||||||
import org.matrix.android.sdk.internal.database.TestRealmConfigurationFactory
|
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
|
||||||
import org.matrix.android.sdk.test.shared.createTimberTestRule
|
|
||||||
import org.matrix.olm.OlmAccount
|
|
||||||
import org.matrix.olm.OlmManager
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.OlmMachine
|
|
||||||
import java.io.File
|
|
||||||
import java.security.KeyStore
|
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
|
||||||
class DynamicElementAndroidToElementRMigrationTest {
|
|
||||||
|
|
||||||
@get:Rule val configurationFactory = TestRealmConfigurationFactory()
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
fun timberTestRule() = createTimberTestRule()
|
|
||||||
|
|
||||||
var context: Context = InstrumentationRegistry.getInstrumentation().context
|
|
||||||
var realm: Realm? = null
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setUp() {
|
|
||||||
// Ensure Olm is initialized
|
|
||||||
OlmManager()
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun tearDown() {
|
|
||||||
realm?.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val keyStore = spyk(KeyStore.getInstance("AndroidKeyStore")).also { it.load(null) }
|
|
||||||
|
|
||||||
private val rustEncryptionConfiguration = RustEncryptionConfiguration(
|
|
||||||
"foo",
|
|
||||||
RealmKeysUtils(
|
|
||||||
context,
|
|
||||||
SecretStoringUtils(context, keyStore, TestBuildVersionSdkIntProvider(), false)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
private val fakeClock = object : Clock {
|
|
||||||
override fun epochMillis() = 0L
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun given_a_valid_crypto_store_realm_file_then_migration_should_be_successful() {
|
|
||||||
testMigrate(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@Ignore("We don't migrate group sessions for now, and it's making this test suite unstable")
|
|
||||||
fun given_a_valid_crypto_store_realm_file_no_lazy_then_migration_should_be_successful() {
|
|
||||||
testMigrate(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun testMigrate(migrateGroupSessions: Boolean) {
|
|
||||||
val targetFile = File(configurationFactory.root, "rust-sdk")
|
|
||||||
|
|
||||||
val realmName = "crypto_store_migration_16.realm"
|
|
||||||
val infoProvider = RustMigrationInfoProvider(
|
|
||||||
targetFile,
|
|
||||||
rustEncryptionConfiguration
|
|
||||||
).apply {
|
|
||||||
migrateMegolmGroupSessions = migrateGroupSessions
|
|
||||||
}
|
|
||||||
val migration = RealmCryptoStoreMigration(fakeClock, infoProvider)
|
|
||||||
|
|
||||||
val realmConfiguration = configurationFactory.createConfiguration(
|
|
||||||
realmName,
|
|
||||||
null,
|
|
||||||
RealmCryptoStoreModule(),
|
|
||||||
migration.schemaVersion,
|
|
||||||
migration
|
|
||||||
)
|
|
||||||
configurationFactory.copyRealmFromAssets(context, realmName, realmName)
|
|
||||||
|
|
||||||
realm = Realm.getInstance(realmConfiguration)
|
|
||||||
val metaData = realm!!.where<CryptoMetadataEntity>().findFirst()!!
|
|
||||||
val userId = metaData.userId!!
|
|
||||||
val deviceId = metaData.deviceId!!
|
|
||||||
val olmAccount = metaData.getOlmAccount()!!
|
|
||||||
|
|
||||||
val machine = OlmMachine(userId, deviceId, targetFile.path, rustEncryptionConfiguration.getDatabasePassphrase())
|
|
||||||
|
|
||||||
assertEquals(olmAccount.identityKeys()[OlmAccount.JSON_KEY_FINGER_PRINT_KEY], machine.identityKeys()["ed25519"])
|
|
||||||
assertNotNull(machine.getBackupKeys())
|
|
||||||
val crossSigningStatus = machine.crossSigningStatus()
|
|
||||||
assertTrue(crossSigningStatus.hasMaster)
|
|
||||||
assertTrue(crossSigningStatus.hasSelfSigning)
|
|
||||||
assertTrue(crossSigningStatus.hasUserSigning)
|
|
||||||
|
|
||||||
if (migrateGroupSessions) {
|
|
||||||
assertTrue("Some outbound sessions should be migrated", machine.roomKeyCounts().total.toInt() > 0)
|
|
||||||
assertTrue("There are some backed-up sessions", machine.roomKeyCounts().backedUp.toInt() > 0)
|
|
||||||
} else {
|
|
||||||
assertTrue(machine.roomKeyCounts().total.toInt() == 0)
|
|
||||||
assertTrue(machine.roomKeyCounts().backedUp.toInt() == 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
// legacy olm sessions should have been deleted
|
|
||||||
val remainingOlmSessions = realm!!.where<OlmSessionEntity>().findAll().size
|
|
||||||
assertEquals("legacy olm sessions should have been removed from store", 0, remainingOlmSessions)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,92 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.database
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.test.platform.app.InstrumentationRegistry
|
|
||||||
import io.mockk.spyk
|
|
||||||
import io.realm.Realm
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.matrix.android.sdk.TestBuildVersionSdkIntProvider
|
|
||||||
import org.matrix.android.sdk.api.securestorage.SecretStoringUtils
|
|
||||||
import org.matrix.android.sdk.internal.crypto.RustEncryptionConfiguration
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreMigration
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RealmCryptoStoreModule
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.RustMigrationInfoProvider
|
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
|
||||||
import org.matrix.olm.OlmManager
|
|
||||||
import java.io.File
|
|
||||||
import java.security.KeyStore
|
|
||||||
|
|
||||||
class CryptoSanityMigrationTest {
|
|
||||||
@get:Rule val configurationFactory = TestRealmConfigurationFactory()
|
|
||||||
|
|
||||||
lateinit var context: Context
|
|
||||||
var realm: Realm? = null
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setUp() {
|
|
||||||
// Ensure Olm is initialized
|
|
||||||
OlmManager()
|
|
||||||
context = InstrumentationRegistry.getInstrumentation().context
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun tearDown() {
|
|
||||||
realm?.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
private val keyStore = spyk(KeyStore.getInstance("AndroidKeyStore")).also { it.load(null) }
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun cryptoDatabaseShouldMigrateGracefully() {
|
|
||||||
val realmName = "crypto_store_20.realm"
|
|
||||||
|
|
||||||
val rustMigrationInfo = RustMigrationInfoProvider(
|
|
||||||
File(configurationFactory.root, "test_rust"),
|
|
||||||
RustEncryptionConfiguration(
|
|
||||||
"foo",
|
|
||||||
RealmKeysUtils(
|
|
||||||
context,
|
|
||||||
SecretStoringUtils(context, keyStore, TestBuildVersionSdkIntProvider(), false)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
val migration = RealmCryptoStoreMigration(
|
|
||||||
object : Clock {
|
|
||||||
override fun epochMillis(): Long {
|
|
||||||
return 0L
|
|
||||||
}
|
|
||||||
},
|
|
||||||
rustMigrationInfo
|
|
||||||
)
|
|
||||||
|
|
||||||
val realmConfiguration = configurationFactory.createConfiguration(
|
|
||||||
realmName,
|
|
||||||
"7b9a21a8a311e85d75b069a343c23fc952fc3fec5e0c83ecfa13f24b787479c487c3ed587db3dd1f5805d52041fc0ac246516e94b27ffa699ff928622e621aca",
|
|
||||||
RealmCryptoStoreModule(),
|
|
||||||
migration.schemaVersion,
|
|
||||||
migration
|
|
||||||
)
|
|
||||||
configurationFactory.copyRealmFromAssets(context, realmName, realmName)
|
|
||||||
|
|
||||||
realm = Realm.getInstance(realmConfiguration)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -39,7 +39,6 @@ import org.matrix.android.sdk.internal.network.ApiInterceptor
|
||||||
import org.matrix.android.sdk.internal.network.UserAgentHolder
|
import org.matrix.android.sdk.internal.network.UserAgentHolder
|
||||||
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
||||||
import org.matrix.olm.OlmManager
|
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -59,7 +58,6 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) {
|
||||||
@Inject internal lateinit var debugService: DebugService
|
@Inject internal lateinit var debugService: DebugService
|
||||||
@Inject internal lateinit var userAgentHolder: UserAgentHolder
|
@Inject internal lateinit var userAgentHolder: UserAgentHolder
|
||||||
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
||||||
@Inject internal lateinit var olmManager: OlmManager
|
|
||||||
@Inject internal lateinit var sessionManager: SessionManager
|
@Inject internal lateinit var sessionManager: SessionManager
|
||||||
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
||||||
@Inject internal lateinit var apiInterceptor: ApiInterceptor
|
@Inject internal lateinit var apiInterceptor: ApiInterceptor
|
||||||
|
|
|
@ -18,7 +18,6 @@ package org.matrix.android.sdk.api.session.crypto
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
||||||
import org.matrix.olm.OlmException
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a crypto error response.
|
* Represents a crypto error response.
|
||||||
|
@ -34,8 +33,6 @@ sealed class MXCryptoError : Throwable() {
|
||||||
val detailedErrorDescription: String? = null
|
val detailedErrorDescription: String? = null
|
||||||
) : MXCryptoError()
|
) : MXCryptoError()
|
||||||
|
|
||||||
data class OlmError(val olmException: OlmException) : MXCryptoError()
|
|
||||||
|
|
||||||
data class UnknownDevice(val deviceList: MXUsersDevicesMap<CryptoDeviceInfo>) : MXCryptoError()
|
data class UnknownDevice(val deviceList: MXUsersDevicesMap<CryptoDeviceInfo>) : MXCryptoError()
|
||||||
|
|
||||||
enum class ErrorType {
|
enum class ErrorType {
|
||||||
|
|
|
@ -53,7 +53,6 @@ import org.matrix.android.sdk.api.util.toOptional
|
||||||
import org.matrix.android.sdk.internal.coroutines.builder.safeInvokeOnClose
|
import org.matrix.android.sdk.internal.coroutines.builder.safeInvokeOnClose
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.DefaultKeysAlgorithmAndData
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.DefaultKeysAlgorithmAndData
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysAlgorithmAndData
|
import org.matrix.android.sdk.internal.crypto.keysbackup.model.rest.KeysAlgorithmAndData
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
|
||||||
import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.SasVerification
|
import org.matrix.android.sdk.internal.crypto.verification.SasVerification
|
||||||
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
|
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
|
||||||
|
@ -318,22 +317,6 @@ internal class OlmMachine @Inject constructor(
|
||||||
inner.receiveVerificationEvent(serializedEvent, roomId)
|
inner.receiveVerificationEvent(serializedEvent, roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Used for lazy migration of inboundGroupSession from EA to ER.
|
|
||||||
*/
|
|
||||||
suspend fun importRoomKey(inbound: MXInboundMegolmSessionWrapper): Result<Unit> {
|
|
||||||
Timber.v("Migration:: Tentative lazy migration")
|
|
||||||
return withContext(coroutineDispatchers.io) {
|
|
||||||
val export = inbound.exportKeys()
|
|
||||||
?: return@withContext Result.failure(Exception("Failed to export key"))
|
|
||||||
val result = importDecryptedKeys(listOf(export), null).also {
|
|
||||||
Timber.v("Migration:: Tentative lazy migration result: ${it.totalNumberOfKeys}")
|
|
||||||
}
|
|
||||||
if (result.totalNumberOfKeys == 1) return@withContext Result.success(Unit)
|
|
||||||
return@withContext Result.failure(Exception("Import failed"))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark the given list of users to be tracked, triggering a key query request for them.
|
* Mark the given list of users to be tracked, triggering a key query request for them.
|
||||||
*
|
*
|
||||||
|
|
|
@ -504,15 +504,8 @@ internal class RustCryptoService @Inject constructor(
|
||||||
val content = event.content?.toModel<EncryptedEventContent>() ?: throw mxCryptoError
|
val content = event.content?.toModel<EncryptedEventContent>() ?: throw mxCryptoError
|
||||||
val roomId = event.roomId
|
val roomId = event.roomId
|
||||||
val sessionId = content.sessionId
|
val sessionId = content.sessionId
|
||||||
val senderKey = content.senderKey
|
|
||||||
if (roomId != null && sessionId != null) {
|
if (roomId != null && sessionId != null) {
|
||||||
// try to perform a lazy migration from legacy store
|
perSessionBackupQueryRateLimiter.tryFromBackupIfPossible(sessionId, roomId)
|
||||||
val legacy = tryOrNull("Failed to access legacy crypto store") {
|
|
||||||
cryptoStore.getInboundGroupSession(sessionId, senderKey.orEmpty())
|
|
||||||
}
|
|
||||||
if (legacy == null || olmMachine.importRoomKey(legacy).isFailure) {
|
|
||||||
perSessionBackupQueryRateLimiter.tryFromBackupIfPossible(sessionId, roomId)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
throw mxCryptoError
|
throw mxCryptoError
|
||||||
|
@ -851,9 +844,9 @@ internal class RustCryptoService @Inject constructor(
|
||||||
override fun removeSessionListener(listener: NewSessionListener) {
|
override fun removeSessionListener(listener: NewSessionListener) {
|
||||||
megolmSessionImportManager.removeListener(listener)
|
megolmSessionImportManager.removeListener(listener)
|
||||||
}
|
}
|
||||||
/* ==========================================================================================
|
/* ==========================================================================================
|
||||||
* DEBUG INFO
|
* DEBUG INFO
|
||||||
* ========================================================================================== */
|
* ========================================================================================== */
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "DefaultCryptoService of $myUserId ($deviceId)"
|
return "DefaultCryptoService of $myUserId ($deviceId)"
|
||||||
|
|
|
@ -65,7 +65,6 @@ import org.matrix.android.sdk.internal.crypto.network.RequestSender
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
|
||||||
import org.matrix.olm.OlmException
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.Request
|
import org.matrix.rustcomponents.sdk.crypto.Request
|
||||||
import org.matrix.rustcomponents.sdk.crypto.RequestType
|
import org.matrix.rustcomponents.sdk.crypto.RequestType
|
||||||
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
||||||
|
@ -840,8 +839,8 @@ internal class RustKeyBackupService @Inject constructor(
|
||||||
try {
|
try {
|
||||||
olmMachine.enableBackupV1(retrievedMegolmBackupAuthData.publicKey, keysVersionResult.version)
|
olmMachine.enableBackupV1(retrievedMegolmBackupAuthData.publicKey, keysVersionResult.version)
|
||||||
keysBackupVersion = keysVersionResult
|
keysBackupVersion = keysVersionResult
|
||||||
} catch (e: OlmException) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "OlmException")
|
Timber.e(e, "Exception")
|
||||||
keysBackupStateManager.state = KeysBackupState.Disabled
|
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,98 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.model
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
|
||||||
import org.matrix.android.sdk.internal.crypto.MegolmSessionData
|
|
||||||
import org.matrix.olm.OlmInboundGroupSession
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
data class MXInboundMegolmSessionWrapper(
|
|
||||||
// olm object
|
|
||||||
val session: OlmInboundGroupSession,
|
|
||||||
// data about the session
|
|
||||||
val sessionData: InboundGroupSessionData
|
|
||||||
) {
|
|
||||||
// shortcut
|
|
||||||
val roomId = sessionData.roomId
|
|
||||||
val senderKey = sessionData.senderKey
|
|
||||||
val safeSessionId = tryOrNull("Fail to get megolm session Id") { session.sessionIdentifier() }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the inbound group session keys.
|
|
||||||
* @param index the index to export. If null, the first known index will be used
|
|
||||||
* @return the inbound group session as MegolmSessionData if the operation succeeds
|
|
||||||
*/
|
|
||||||
internal fun exportKeys(index: Long? = null): MegolmSessionData? {
|
|
||||||
return try {
|
|
||||||
val keysClaimed = sessionData.keysClaimed ?: return null
|
|
||||||
val wantedIndex = index ?: session.firstKnownIndex
|
|
||||||
|
|
||||||
MegolmSessionData(
|
|
||||||
senderClaimedEd25519Key = sessionData.keysClaimed?.get("ed25519"),
|
|
||||||
forwardingCurve25519KeyChain = sessionData.forwardingCurve25519KeyChain?.toList().orEmpty(),
|
|
||||||
sessionKey = session.export(wantedIndex),
|
|
||||||
senderClaimedKeys = keysClaimed,
|
|
||||||
roomId = sessionData.roomId,
|
|
||||||
sessionId = session.sessionIdentifier(),
|
|
||||||
senderKey = senderKey,
|
|
||||||
algorithm = MXCRYPTO_ALGORITHM_MEGOLM,
|
|
||||||
sharedHistory = sessionData.sharedHistory
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "## Failed to export megolm : sessionID ${tryOrNull { session.sessionIdentifier() }} failed")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @exportFormat true if the megolm keys are in export format
|
|
||||||
* (ie, they lack an ed25519 signature)
|
|
||||||
*/
|
|
||||||
@Throws
|
|
||||||
internal fun newFromMegolmData(megolmSessionData: MegolmSessionData, exportFormat: Boolean): MXInboundMegolmSessionWrapper {
|
|
||||||
val exportedKey = megolmSessionData.sessionKey ?: throw IllegalArgumentException("key data not found")
|
|
||||||
val inboundSession = if (exportFormat) {
|
|
||||||
OlmInboundGroupSession.importSession(exportedKey)
|
|
||||||
} else {
|
|
||||||
OlmInboundGroupSession(exportedKey)
|
|
||||||
}
|
|
||||||
.also {
|
|
||||||
if (it.sessionIdentifier() != megolmSessionData.sessionId) {
|
|
||||||
it.releaseSession()
|
|
||||||
throw IllegalStateException("Mismatched group session Id")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val data = InboundGroupSessionData(
|
|
||||||
roomId = megolmSessionData.roomId,
|
|
||||||
senderKey = megolmSessionData.senderKey,
|
|
||||||
keysClaimed = megolmSessionData.senderClaimedKeys,
|
|
||||||
forwardingCurve25519KeyChain = megolmSessionData.forwardingCurve25519KeyChain,
|
|
||||||
sharedHistory = megolmSessionData.sharedHistory,
|
|
||||||
trusted = false
|
|
||||||
)
|
|
||||||
|
|
||||||
return MXInboundMegolmSessionWrapper(
|
|
||||||
inboundSession,
|
|
||||||
data
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.model
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal data class MXKey(
|
|
||||||
/**
|
|
||||||
* The type of the key (in the example: "signed_curve25519").
|
|
||||||
*/
|
|
||||||
val type: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The id of the key (in the example: "AAAAFw").
|
|
||||||
*/
|
|
||||||
private val keyId: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The key (in the example: "IjwIcskng7YjYcn0tS8TUOT2OHHtBSfMpcfIczCgXj4").
|
|
||||||
*/
|
|
||||||
val value: String,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* signature user Id to [deviceid][signature].
|
|
||||||
*/
|
|
||||||
private val signatures: Map<String, Map<String, String>>,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* We have to store the original json because it can contain other fields
|
|
||||||
* that we don't support yet but they would be needed to check signatures.
|
|
||||||
*/
|
|
||||||
private val rawMap: JsonDict
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the signed data map
|
|
||||||
*/
|
|
||||||
fun signalableJSONDictionary(): Map<String, Any> {
|
|
||||||
return rawMap.filter {
|
|
||||||
it.key != "signatures" && it.key != "unsigned"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a signature for an user Id and a signkey.
|
|
||||||
*
|
|
||||||
* @param userId the user id
|
|
||||||
* @param signkey the sign key
|
|
||||||
* @return the signature
|
|
||||||
*/
|
|
||||||
fun signatureForUserId(userId: String, signkey: String): String? {
|
|
||||||
// sanity checks
|
|
||||||
if (userId.isNotBlank() && signkey.isNotBlank()) {
|
|
||||||
return signatures[userId]?.get(signkey)
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
/**
|
|
||||||
* Key types.
|
|
||||||
*/
|
|
||||||
const val KEY_CURVE_25519_TYPE = "curve25519"
|
|
||||||
const val KEY_SIGNED_CURVE_25519_TYPE = "signed_curve25519"
|
|
||||||
// const val KEY_ED_25519_TYPE = "ed25519"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert a map to a MXKey.
|
|
||||||
*
|
|
||||||
* @param map the map to convert
|
|
||||||
*
|
|
||||||
* Json Example:
|
|
||||||
*
|
|
||||||
* <pre>
|
|
||||||
* "signed_curve25519:AAAAFw": {
|
|
||||||
* "key": "IjwIcskng7YjYcn0tS8TUOT2OHHtBSfMpcfIczCgXj4",
|
|
||||||
* "fallback" : true|false
|
|
||||||
* "signatures": {
|
|
||||||
* "@userId:matrix.org": {
|
|
||||||
* "ed25519:GMJRREOASV": "EUjp6pXzK9u3SDFR\/qLbzpOi3bEREeI6qMnKzXu992HsfuDDZftfJfiUXv9b\/Hqq1og4qM\/vCQJGTHAWMmgkCg"
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* </pre>
|
|
||||||
*
|
|
||||||
* into several val members
|
|
||||||
*/
|
|
||||||
fun from(map: Map<String, JsonDict>?): MXKey? {
|
|
||||||
if (map?.isNotEmpty() == true) {
|
|
||||||
val firstKey = map.keys.first()
|
|
||||||
|
|
||||||
val components = firstKey.split(":").dropLastWhile { it.isEmpty() }
|
|
||||||
|
|
||||||
if (components.size == 2) {
|
|
||||||
val params = map[firstKey]
|
|
||||||
if (params != null) {
|
|
||||||
if (params["key"] is String) {
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
return MXKey(
|
|
||||||
type = components[0],
|
|
||||||
keyId = components[1],
|
|
||||||
value = params["key"] as String,
|
|
||||||
signatures = params["signatures"] as Map<String, Map<String, String>>,
|
|
||||||
rawMap = params
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error case
|
|
||||||
Timber.e("## Unable to parse map")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.model
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
internal data class MXOlmSessionResult(
|
|
||||||
/**
|
|
||||||
* the device.
|
|
||||||
*/
|
|
||||||
val deviceInfo: CryptoDeviceInfo,
|
|
||||||
/**
|
|
||||||
* Base64 olm session id.
|
|
||||||
* null if no session could be established.
|
|
||||||
*/
|
|
||||||
var sessionId: String?
|
|
||||||
) : Serializable
|
|
|
@ -1,27 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.model
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
|
|
||||||
|
|
||||||
internal fun <T> MXUsersDevicesMap<T>.toDebugString() =
|
|
||||||
map.entries.joinToString { "${it.key} [${it.value.keys.joinToString { it }}]" }
|
|
||||||
|
|
||||||
internal fun <T> MXUsersDevicesMap<T>.toDebugCount() =
|
|
||||||
map.entries.fold(0) { acc, new ->
|
|
||||||
acc + new.value.keys.size
|
|
||||||
}
|
|
|
@ -1,151 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.model
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
import org.matrix.android.sdk.internal.crypto.MegolmSessionData
|
|
||||||
import org.matrix.olm.OlmInboundGroupSession
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class adds more context to a OlmInboundGroupSession object.
|
|
||||||
* This allows additional checks. The class implements Serializable so that the context can be stored.
|
|
||||||
*/
|
|
||||||
internal class OlmInboundGroupSessionWrapper : Serializable {
|
|
||||||
|
|
||||||
// The associated olm inbound group session.
|
|
||||||
var olmInboundGroupSession: OlmInboundGroupSession? = null
|
|
||||||
|
|
||||||
// The room in which this session is used.
|
|
||||||
var roomId: String? = null
|
|
||||||
|
|
||||||
// The base64-encoded curve25519 key of the sender.
|
|
||||||
var senderKey: String? = null
|
|
||||||
|
|
||||||
// Other keys the sender claims.
|
|
||||||
var keysClaimed: Map<String, String>? = null
|
|
||||||
|
|
||||||
// Devices which forwarded this session to us (normally empty).
|
|
||||||
var forwardingCurve25519KeyChain: List<String>? = ArrayList()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the first known message index
|
|
||||||
*/
|
|
||||||
val firstKnownIndex: Long?
|
|
||||||
get() {
|
|
||||||
if (null != olmInboundGroupSession) {
|
|
||||||
try {
|
|
||||||
return olmInboundGroupSession!!.firstKnownIndex
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "## getFirstKnownIndex() : getFirstKnownIndex failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param sessionKey the session key
|
|
||||||
* @param isImported true if it is an imported session key
|
|
||||||
*/
|
|
||||||
constructor(sessionKey: String, isImported: Boolean) {
|
|
||||||
try {
|
|
||||||
if (!isImported) {
|
|
||||||
olmInboundGroupSession = OlmInboundGroupSession(sessionKey)
|
|
||||||
} else {
|
|
||||||
olmInboundGroupSession = OlmInboundGroupSession.importSession(sessionKey)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "Cannot create")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance from the provided keys map.
|
|
||||||
*
|
|
||||||
* @param megolmSessionData the megolm session data
|
|
||||||
* @throws Exception if the data are invalid
|
|
||||||
*/
|
|
||||||
@Throws(Exception::class)
|
|
||||||
constructor(megolmSessionData: MegolmSessionData) {
|
|
||||||
try {
|
|
||||||
olmInboundGroupSession = OlmInboundGroupSession.importSession(megolmSessionData.sessionKey!!)
|
|
||||||
|
|
||||||
if (olmInboundGroupSession!!.sessionIdentifier() != megolmSessionData.sessionId) {
|
|
||||||
throw Exception("Mismatched group session Id")
|
|
||||||
}
|
|
||||||
|
|
||||||
senderKey = megolmSessionData.senderKey
|
|
||||||
keysClaimed = megolmSessionData.senderClaimedKeys
|
|
||||||
roomId = megolmSessionData.roomId
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw Exception(e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the inbound group session keys.
|
|
||||||
*
|
|
||||||
* @return the inbound group session as MegolmSessionData if the operation succeeds
|
|
||||||
*/
|
|
||||||
fun exportKeys(): MegolmSessionData? {
|
|
||||||
return try {
|
|
||||||
if (null == forwardingCurve25519KeyChain) {
|
|
||||||
forwardingCurve25519KeyChain = ArrayList()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keysClaimed == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
MegolmSessionData(
|
|
||||||
senderClaimedEd25519Key = keysClaimed?.get("ed25519"),
|
|
||||||
forwardingCurve25519KeyChain = ArrayList(forwardingCurve25519KeyChain!!),
|
|
||||||
senderKey = senderKey,
|
|
||||||
senderClaimedKeys = keysClaimed,
|
|
||||||
roomId = roomId,
|
|
||||||
sessionId = olmInboundGroupSession!!.sessionIdentifier(),
|
|
||||||
sessionKey = olmInboundGroupSession!!.export(olmInboundGroupSession!!.firstKnownIndex),
|
|
||||||
algorithm = MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "## export() : senderKey $senderKey failed")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the session for a message index.
|
|
||||||
*
|
|
||||||
* @param messageIndex the message index
|
|
||||||
* @return the exported data
|
|
||||||
*/
|
|
||||||
fun exportSession(messageIndex: Long): String? {
|
|
||||||
if (null != olmInboundGroupSession) {
|
|
||||||
try {
|
|
||||||
return olmInboundGroupSession!!.export(messageIndex)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "## exportSession() : export failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,158 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.model
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
import org.matrix.android.sdk.internal.crypto.MegolmSessionData
|
|
||||||
import org.matrix.olm.OlmInboundGroupSession
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.Serializable
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class adds more context to a OlmInboundGroupSession object.
|
|
||||||
* This allows additional checks. The class implements Serializable so that the context can be stored.
|
|
||||||
*/
|
|
||||||
// Note used anymore, just for database migration
|
|
||||||
// Deprecated("Use MXInboundMegolmSessionWrapper")
|
|
||||||
internal class OlmInboundGroupSessionWrapper2 : Serializable {
|
|
||||||
|
|
||||||
// The associated olm inbound group session.
|
|
||||||
var olmInboundGroupSession: OlmInboundGroupSession? = null
|
|
||||||
|
|
||||||
// The room in which this session is used.
|
|
||||||
var roomId: String? = null
|
|
||||||
|
|
||||||
// The base64-encoded curve25519 key of the sender.
|
|
||||||
var senderKey: String? = null
|
|
||||||
|
|
||||||
// Other keys the sender claims.
|
|
||||||
var keysClaimed: Map<String, String>? = null
|
|
||||||
|
|
||||||
// Devices which forwarded this session to us (normally empty).
|
|
||||||
var forwardingCurve25519KeyChain: List<String>? = ArrayList()
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the first known message index
|
|
||||||
*/
|
|
||||||
val firstKnownIndex: Long?
|
|
||||||
get() {
|
|
||||||
return try {
|
|
||||||
olmInboundGroupSession?.firstKnownIndex
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "## getFirstKnownIndex() : getFirstKnownIndex failed")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor.
|
|
||||||
*
|
|
||||||
* @param sessionKey the session key
|
|
||||||
* @param isImported true if it is an imported session key
|
|
||||||
*/
|
|
||||||
constructor(sessionKey: String, isImported: Boolean) {
|
|
||||||
try {
|
|
||||||
if (!isImported) {
|
|
||||||
olmInboundGroupSession = OlmInboundGroupSession(sessionKey)
|
|
||||||
} else {
|
|
||||||
olmInboundGroupSession = OlmInboundGroupSession.importSession(sessionKey)
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "Cannot create")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
// empty
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new instance from the provided keys map.
|
|
||||||
*
|
|
||||||
* @param megolmSessionData the megolm session data
|
|
||||||
* @throws Exception if the data are invalid
|
|
||||||
*/
|
|
||||||
@Throws(Exception::class)
|
|
||||||
constructor(megolmSessionData: MegolmSessionData) {
|
|
||||||
try {
|
|
||||||
val safeSessionKey = megolmSessionData.sessionKey ?: throw Exception("invalid data")
|
|
||||||
olmInboundGroupSession = OlmInboundGroupSession.importSession(safeSessionKey)
|
|
||||||
.also {
|
|
||||||
if (it.sessionIdentifier() != megolmSessionData.sessionId) {
|
|
||||||
throw Exception("Mismatched group session Id")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
senderKey = megolmSessionData.senderKey
|
|
||||||
keysClaimed = megolmSessionData.senderClaimedKeys
|
|
||||||
roomId = megolmSessionData.roomId
|
|
||||||
} catch (e: Exception) {
|
|
||||||
throw Exception(e.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the inbound group session keys.
|
|
||||||
* @param index the index to export. If null, the first known index will be used
|
|
||||||
*
|
|
||||||
* @return the inbound group session as MegolmSessionData if the operation succeeds
|
|
||||||
*/
|
|
||||||
fun exportKeys(index: Long? = null): MegolmSessionData? {
|
|
||||||
return try {
|
|
||||||
if (null == forwardingCurve25519KeyChain) {
|
|
||||||
forwardingCurve25519KeyChain = ArrayList()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (keysClaimed == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val safeOlmInboundGroupSession = olmInboundGroupSession ?: return null
|
|
||||||
|
|
||||||
val wantedIndex = index ?: safeOlmInboundGroupSession.firstKnownIndex
|
|
||||||
|
|
||||||
MegolmSessionData(
|
|
||||||
senderClaimedEd25519Key = keysClaimed?.get("ed25519"),
|
|
||||||
forwardingCurve25519KeyChain = forwardingCurve25519KeyChain?.toList().orEmpty(),
|
|
||||||
senderKey = senderKey,
|
|
||||||
senderClaimedKeys = keysClaimed,
|
|
||||||
roomId = roomId,
|
|
||||||
sessionId = safeOlmInboundGroupSession.sessionIdentifier(),
|
|
||||||
sessionKey = safeOlmInboundGroupSession.export(wantedIndex),
|
|
||||||
algorithm = MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "## export() : senderKey $senderKey failed")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Export the session for a message index.
|
|
||||||
*
|
|
||||||
* @param messageIndex the message index
|
|
||||||
* @return the exported data
|
|
||||||
*/
|
|
||||||
fun exportSession(messageIndex: Long): String? {
|
|
||||||
return try {
|
|
||||||
return olmInboundGroupSession?.export(messageIndex)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "## exportSession() : export failed")
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.model
|
|
||||||
|
|
||||||
import kotlinx.coroutines.sync.Mutex
|
|
||||||
import org.matrix.olm.OlmSession
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Encapsulate a OlmSession and a last received message Timestamp.
|
|
||||||
*/
|
|
||||||
internal data class OlmSessionWrapper(
|
|
||||||
// The associated olm session.
|
|
||||||
val olmSession: OlmSession,
|
|
||||||
// Timestamp at which the session last received a message.
|
|
||||||
var lastReceivedMessageTs: Long = 0,
|
|
||||||
|
|
||||||
val mutex: Mutex = Mutex()
|
|
||||||
) {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify that a message has been received on this olm session so that it updates `lastReceivedMessageTs`.
|
|
||||||
*/
|
|
||||||
fun onMessageReceived(currentTimeMillis: Long) {
|
|
||||||
lastReceivedMessageTs = currentTimeMillis
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,28 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.model
|
|
||||||
|
|
||||||
import org.matrix.olm.OlmOutboundGroupSession
|
|
||||||
|
|
||||||
internal data class OutboundGroupSessionWrapper(
|
|
||||||
val outboundGroupSession: OlmOutboundGroupSession,
|
|
||||||
val creationTime: Long,
|
|
||||||
/**
|
|
||||||
* As per MSC 3061, declares if this key could be shared when inviting a new user to the room.
|
|
||||||
*/
|
|
||||||
val sharedHistory: Boolean = false
|
|
||||||
)
|
|
|
@ -44,9 +44,7 @@ import org.matrix.android.sdk.api.util.toBase64NoPadding
|
||||||
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
import org.matrix.android.sdk.internal.crypto.SecretShareManager
|
||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.generatePrivateKeyWithPassword
|
import org.matrix.android.sdk.internal.crypto.keysbackup.generatePrivateKeyWithPassword
|
||||||
import org.matrix.android.sdk.internal.crypto.tools.HkdfSha256
|
import org.matrix.android.sdk.internal.crypto.tools.HkdfSha256
|
||||||
import org.matrix.android.sdk.internal.crypto.tools.withOlmDecryption
|
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.olm.OlmPkMessage
|
|
||||||
import java.security.SecureRandom
|
import java.security.SecureRandom
|
||||||
import javax.crypto.Cipher
|
import javax.crypto.Cipher
|
||||||
import javax.crypto.Mac
|
import javax.crypto.Mac
|
||||||
|
@ -322,9 +320,13 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
|
|
||||||
val algorithm = key.keyInfo.content
|
val algorithm = key.keyInfo.content
|
||||||
if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
|
if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
|
||||||
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
|
// TODO BMA
|
||||||
|
// val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
|
||||||
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
|
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
|
||||||
// decrypt from recovery key
|
// decrypt from recovery key
|
||||||
|
"TODO"
|
||||||
|
// TODO BMA
|
||||||
|
/*
|
||||||
withOlmDecryption { olmPkDecryption ->
|
withOlmDecryption { olmPkDecryption ->
|
||||||
olmPkDecryption.setPrivateKey(keySpec.privateKey)
|
olmPkDecryption.setPrivateKey(keySpec.privateKey)
|
||||||
olmPkDecryption.decrypt(OlmPkMessage()
|
olmPkDecryption.decrypt(OlmPkMessage()
|
||||||
|
@ -335,6 +337,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
} else if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) {
|
} else if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) {
|
||||||
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
|
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
|
||||||
|
|
|
@ -23,7 +23,6 @@ import org.matrix.android.sdk.api.session.crypto.model.CryptoRoomInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
|
import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventContent
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -143,14 +142,4 @@ interface IMXCommonCryptoStore {
|
||||||
* @return the device or null if not found
|
* @return the device or null if not found
|
||||||
*/
|
*/
|
||||||
fun deviceWithIdentityKey(userId: String, identityKey: String): CryptoDeviceInfo?
|
fun deviceWithIdentityKey(userId: String, identityKey: String): CryptoDeviceInfo?
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an inbound group session.
|
|
||||||
* Used in rust for lazy migration
|
|
||||||
*
|
|
||||||
* @param sessionId the session identifier.
|
|
||||||
* @param senderKey the base64-encoded curve25519 key of the sender.
|
|
||||||
* @return an inbound group session.
|
|
||||||
*/
|
|
||||||
fun getInboundGroupSession(sessionId: String, senderKey: String): MXInboundMegolmSessionWrapper?
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,6 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptionEventCo
|
||||||
import org.matrix.android.sdk.api.util.Optional
|
import org.matrix.android.sdk.api.util.Optional
|
||||||
import org.matrix.android.sdk.api.util.toOptional
|
import org.matrix.android.sdk.api.util.toOptional
|
||||||
import org.matrix.android.sdk.internal.crypto.OlmMachine
|
import org.matrix.android.sdk.internal.crypto.OlmMachine
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
import org.matrix.android.sdk.internal.crypto.store.db.CryptoStoreAggregator
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.doRealmTransaction
|
import org.matrix.android.sdk.internal.crypto.store.db.doRealmTransaction
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.doRealmTransactionAsync
|
import org.matrix.android.sdk.internal.crypto.store.db.doRealmTransactionAsync
|
||||||
|
@ -49,11 +48,8 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields
|
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
|
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntityFields
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntityFields
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.createPrimaryKey
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.query.getById
|
import org.matrix.android.sdk.internal.crypto.store.db.query.getById
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.query.getOrCreate
|
import org.matrix.android.sdk.internal.crypto.store.db.query.getOrCreate
|
||||||
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
||||||
|
@ -70,7 +66,7 @@ private val loggerTag = LoggerTag("RealmCryptoStore", LoggerTag.CRYPTO)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* In the transition phase, the rust SDK is still using parts to the realm crypto store,
|
* In the transition phase, the rust SDK is still using parts to the realm crypto store,
|
||||||
* this should be removed after full migration.
|
* this should be removed after full migration. TODO BMA
|
||||||
*/
|
*/
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class RustCryptoStore @Inject constructor(
|
internal class RustCryptoStore @Inject constructor(
|
||||||
|
@ -86,6 +82,7 @@ internal class RustCryptoStore @Inject constructor(
|
||||||
// still needed on rust due to the global crypto settings
|
// still needed on rust due to the global crypto settings
|
||||||
init {
|
init {
|
||||||
// Ensure CryptoMetadataEntity is inserted in DB
|
// Ensure CryptoMetadataEntity is inserted in DB
|
||||||
|
// TODO BMA
|
||||||
doRealmTransaction("init", realmConfiguration) { realm ->
|
doRealmTransaction("init", realmConfiguration) { realm ->
|
||||||
var currentMetadata = realm.where<CryptoMetadataEntity>().findFirst()
|
var currentMetadata = realm.where<CryptoMetadataEntity>().findFirst()
|
||||||
|
|
||||||
|
@ -134,20 +131,6 @@ internal class RustCryptoStore @Inject constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Needed for lazy migration of sessions from the legacy store.
|
|
||||||
*/
|
|
||||||
override fun getInboundGroupSession(sessionId: String, senderKey: String): MXInboundMegolmSessionWrapper? {
|
|
||||||
val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey)
|
|
||||||
|
|
||||||
return doWithRealm(realmConfiguration) { realm ->
|
|
||||||
realm.where<OlmInboundGroupSessionEntity>()
|
|
||||||
.equalTo(OlmInboundGroupSessionEntityFields.PRIMARY_KEY, key)
|
|
||||||
.findFirst()
|
|
||||||
?.toModel()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ================================================
|
// ================================================
|
||||||
// Things that should be migrated to another store than realm
|
// Things that should be migrated to another store than realm
|
||||||
// ================================================
|
// ================================================
|
||||||
|
|
|
@ -17,31 +17,7 @@
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db
|
package org.matrix.android.sdk.internal.crypto.store.db
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
import io.realm.DynamicRealm
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo001Legacy
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo002Legacy
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo003RiotX
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo004
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo005
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo006
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo007
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo008
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo009
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo010
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo011
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo012
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo013
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo014
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo015
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo016
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo018
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo019
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo020
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo021
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo022
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo023
|
|
||||||
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
|
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -51,11 +27,9 @@ import javax.inject.Inject
|
||||||
* 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6).
|
* 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6).
|
||||||
*/
|
*/
|
||||||
internal class RealmCryptoStoreMigration @Inject constructor(
|
internal class RealmCryptoStoreMigration @Inject constructor(
|
||||||
private val clock: Clock,
|
|
||||||
private val rustMigrationInfoProvider: RustMigrationInfoProvider,
|
|
||||||
) : MatrixRealmMigration(
|
) : MatrixRealmMigration(
|
||||||
dbName = "Crypto",
|
dbName = "Crypto",
|
||||||
schemaVersion = 23L,
|
schemaVersion = 24L,
|
||||||
) {
|
) {
|
||||||
/**
|
/**
|
||||||
* Forces all RealmCryptoStoreMigration instances to be equal.
|
* Forces all RealmCryptoStoreMigration instances to be equal.
|
||||||
|
@ -65,33 +39,8 @@ internal class RealmCryptoStoreMigration @Inject constructor(
|
||||||
override fun hashCode() = 5000
|
override fun hashCode() = 5000
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm, oldVersion: Long) {
|
override fun doMigrate(realm: DynamicRealm, oldVersion: Long) {
|
||||||
if (oldVersion < 1) MigrateCryptoTo001Legacy(realm).perform()
|
// Delete the whole DB
|
||||||
if (oldVersion < 2) MigrateCryptoTo002Legacy(realm).perform()
|
// TODO BMA
|
||||||
if (oldVersion < 3) MigrateCryptoTo003RiotX(realm).perform()
|
realm.deleteAll()
|
||||||
if (oldVersion < 4) MigrateCryptoTo004(realm).perform()
|
|
||||||
if (oldVersion < 5) MigrateCryptoTo005(realm).perform()
|
|
||||||
if (oldVersion < 6) MigrateCryptoTo006(realm).perform()
|
|
||||||
if (oldVersion < 7) MigrateCryptoTo007(realm).perform()
|
|
||||||
if (oldVersion < 8) MigrateCryptoTo008(realm, clock).perform()
|
|
||||||
if (oldVersion < 9) MigrateCryptoTo009(realm).perform()
|
|
||||||
if (oldVersion < 10) MigrateCryptoTo010(realm).perform()
|
|
||||||
if (oldVersion < 11) MigrateCryptoTo011(realm).perform()
|
|
||||||
if (oldVersion < 12) MigrateCryptoTo012(realm).perform()
|
|
||||||
if (oldVersion < 13) MigrateCryptoTo013(realm).perform()
|
|
||||||
if (oldVersion < 14) MigrateCryptoTo014(realm).perform()
|
|
||||||
if (oldVersion < 15) MigrateCryptoTo015(realm).perform()
|
|
||||||
if (oldVersion < 16) MigrateCryptoTo016(realm).perform()
|
|
||||||
if (oldVersion < 17) MigrateCryptoTo017(realm).perform()
|
|
||||||
if (oldVersion < 18) MigrateCryptoTo018(realm).perform()
|
|
||||||
if (oldVersion < 19) MigrateCryptoTo019(realm).perform()
|
|
||||||
if (oldVersion < 20) MigrateCryptoTo020(realm).perform()
|
|
||||||
if (oldVersion < 21) MigrateCryptoTo021(realm).perform()
|
|
||||||
if (oldVersion < 22) MigrateCryptoTo022(
|
|
||||||
realm,
|
|
||||||
rustMigrationInfoProvider.rustDirectory,
|
|
||||||
rustMigrationInfoProvider.rustEncryptionConfiguration,
|
|
||||||
rustMigrationInfoProvider.migrateMegolmGroupSessions
|
|
||||||
).perform()
|
|
||||||
if (oldVersion < 23) MigrateCryptoTo023(realm).perform()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.internal.crypto.RustEncryptionConfiguration
|
|
||||||
import org.matrix.android.sdk.internal.di.SessionRustFilesDirectory
|
|
||||||
import java.io.File
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
internal class RustMigrationInfoProvider @Inject constructor(
|
|
||||||
@SessionRustFilesDirectory
|
|
||||||
val rustDirectory: File,
|
|
||||||
val rustEncryptionConfiguration: RustEncryptionConfiguration
|
|
||||||
) {
|
|
||||||
|
|
||||||
var migrateMegolmGroupSessions: Boolean = false
|
|
||||||
}
|
|
|
@ -1,87 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.mapper
|
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import com.squareup.moshi.Types
|
|
||||||
import io.realm.RealmList
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntity
|
|
||||||
import timber.log.Timber
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
internal class CrossSigningKeysMapper @Inject constructor(moshi: Moshi) {
|
|
||||||
|
|
||||||
private val signaturesAdapter = moshi.adapter<Map<String, Map<String, String>>>(
|
|
||||||
Types.newParameterizedType(
|
|
||||||
Map::class.java,
|
|
||||||
String::class.java,
|
|
||||||
Any::class.java
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
fun update(keyInfo: KeyInfoEntity, cryptoCrossSigningKey: CryptoCrossSigningKey) {
|
|
||||||
// update signatures?
|
|
||||||
keyInfo.signatures = serializeSignatures(cryptoCrossSigningKey.signatures)
|
|
||||||
keyInfo.usages = cryptoCrossSigningKey.usages?.toTypedArray()?.let { RealmList(*it) }
|
|
||||||
?: RealmList()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun map(userId: String?, keyInfo: KeyInfoEntity?): CryptoCrossSigningKey? {
|
|
||||||
val pubKey = keyInfo?.publicKeyBase64 ?: return null
|
|
||||||
return CryptoCrossSigningKey(
|
|
||||||
userId = userId ?: "",
|
|
||||||
keys = mapOf("ed25519:$pubKey" to pubKey),
|
|
||||||
usages = keyInfo.usages.toList(),
|
|
||||||
signatures = deserializeSignatures(keyInfo.signatures),
|
|
||||||
trustLevel = keyInfo.trustLevelEntity?.let {
|
|
||||||
DeviceTrustLevel(
|
|
||||||
crossSigningVerified = it.crossSignedVerified ?: false,
|
|
||||||
locallyVerified = it.locallyVerified ?: false
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun map(keyInfo: CryptoCrossSigningKey): KeyInfoEntity {
|
|
||||||
return KeyInfoEntity().apply {
|
|
||||||
publicKeyBase64 = keyInfo.unpaddedBase64PublicKey
|
|
||||||
usages = keyInfo.usages?.let { RealmList(*it.toTypedArray()) } ?: RealmList()
|
|
||||||
signatures = serializeSignatures(keyInfo.signatures)
|
|
||||||
// TODO how to handle better, check if same keys?
|
|
||||||
// reset trust
|
|
||||||
trustLevelEntity = null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun serializeSignatures(signatures: Map<String, Map<String, String>>?): String {
|
|
||||||
return signaturesAdapter.toJson(signatures)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deserializeSignatures(signatures: String?): Map<String, Map<String, String>>? {
|
|
||||||
if (signatures == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
return try {
|
|
||||||
signaturesAdapter.fromJson(signatures)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal class MigrateCryptoTo001Legacy(realm: DynamicRealm) : RealmMigrator(realm, 1) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
Timber.d("Add field lastReceivedMessageTs (Long) and set the value to 0")
|
|
||||||
|
|
||||||
realm.schema.get("OlmSessionEntity")
|
|
||||||
?.addField(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, Long::class.java)
|
|
||||||
?.transform {
|
|
||||||
it.setLong(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS, 0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,86 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal class MigrateCryptoTo002Legacy(realm: DynamicRealm) : RealmMigrator(realm, 2) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
Timber.d("Update IncomingRoomKeyRequestEntity format: requestBodyString field is exploded into several fields")
|
|
||||||
realm.schema.get("IncomingRoomKeyRequestEntity")
|
|
||||||
?.addFieldIfNotExists("requestBodyAlgorithm", String::class.java)
|
|
||||||
?.addFieldIfNotExists("requestBodyRoomId", String::class.java)
|
|
||||||
?.addFieldIfNotExists("requestBodySenderKey", String::class.java)
|
|
||||||
?.addFieldIfNotExists("requestBodySessionId", String::class.java)
|
|
||||||
?.transform { dynamicObject ->
|
|
||||||
try {
|
|
||||||
val requestBodyString = dynamicObject.getString("requestBodyString")
|
|
||||||
// It was a map before
|
|
||||||
val map: Map<String, String>? = deserializeFromRealm(requestBodyString)
|
|
||||||
|
|
||||||
map?.let {
|
|
||||||
dynamicObject.setString("requestBodyAlgorithm", it["algorithm"])
|
|
||||||
dynamicObject.setString("requestBodyRoomId", it["room_id"])
|
|
||||||
dynamicObject.setString("requestBodySenderKey", it["sender_key"])
|
|
||||||
dynamicObject.setString("requestBodySessionId", it["session_id"])
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?.removeFieldIfExists("requestBodyString")
|
|
||||||
|
|
||||||
Timber.d("Update IncomingRoomKeyRequestEntity format: requestBodyString field is exploded into several fields")
|
|
||||||
realm.schema.get("OutgoingRoomKeyRequestEntity")
|
|
||||||
?.addFieldIfNotExists("requestBodyAlgorithm", String::class.java)
|
|
||||||
?.addFieldIfNotExists("requestBodyRoomId", String::class.java)
|
|
||||||
?.addFieldIfNotExists("requestBodySenderKey", String::class.java)
|
|
||||||
?.addFieldIfNotExists("requestBodySessionId", String::class.java)
|
|
||||||
?.transform { dynamicObject ->
|
|
||||||
try {
|
|
||||||
val requestBodyString = dynamicObject.getString("requestBodyString")
|
|
||||||
// It was a map before
|
|
||||||
val map: Map<String, String>? = deserializeFromRealm(requestBodyString)
|
|
||||||
|
|
||||||
map?.let {
|
|
||||||
dynamicObject.setString("requestBodyAlgorithm", it["algorithm"])
|
|
||||||
dynamicObject.setString("requestBodyRoomId", it["room_id"])
|
|
||||||
dynamicObject.setString("requestBodySenderKey", it["sender_key"])
|
|
||||||
dynamicObject.setString("requestBodySessionId", it["session_id"])
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?.removeFieldIfExists("requestBodyString")
|
|
||||||
|
|
||||||
Timber.d("Create KeysBackupDataEntity")
|
|
||||||
if (!realm.schema.contains("KeysBackupDataEntity")) {
|
|
||||||
realm.schema.create("KeysBackupDataEntity")
|
|
||||||
.addField(KeysBackupDataEntityFields.PRIMARY_KEY, Integer::class.java)
|
|
||||||
.addPrimaryKey(KeysBackupDataEntityFields.PRIMARY_KEY)
|
|
||||||
.setRequired(KeysBackupDataEntityFields.PRIMARY_KEY, true)
|
|
||||||
.addField(KeysBackupDataEntityFields.BACKUP_LAST_SERVER_HASH, String::class.java)
|
|
||||||
.addField(KeysBackupDataEntityFields.BACKUP_LAST_SERVER_NUMBER_OF_KEYS, Integer::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import org.matrix.androidsdk.crypto.data.MXDeviceInfo
|
|
||||||
import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal class MigrateCryptoTo003RiotX(realm: DynamicRealm) : RealmMigrator(realm, 3) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
Timber.d("Migrate to RiotX model")
|
|
||||||
realm.schema.get("CryptoRoomEntity")
|
|
||||||
?.addFieldIfNotExists(CryptoRoomEntityFields.SHOULD_ENCRYPT_FOR_INVITED_MEMBERS, Boolean::class.java)
|
|
||||||
?.setRequiredIfNotAlready(CryptoRoomEntityFields.SHOULD_ENCRYPT_FOR_INVITED_MEMBERS, false)
|
|
||||||
|
|
||||||
// Convert format of MXDeviceInfo, package has to be the same.
|
|
||||||
realm.schema.get("DeviceInfoEntity")
|
|
||||||
?.transform { obj ->
|
|
||||||
try {
|
|
||||||
val oldSerializedData = obj.getString("deviceInfoData")
|
|
||||||
deserializeFromRealm<MXDeviceInfo>(oldSerializedData)?.let { legacyMxDeviceInfo ->
|
|
||||||
val newMxDeviceInfo = org.matrix.android.sdk.api.session.crypto.model.MXDeviceInfo(
|
|
||||||
deviceId = legacyMxDeviceInfo.deviceId,
|
|
||||||
userId = legacyMxDeviceInfo.userId,
|
|
||||||
algorithms = legacyMxDeviceInfo.algorithms,
|
|
||||||
keys = legacyMxDeviceInfo.keys,
|
|
||||||
signatures = legacyMxDeviceInfo.signatures,
|
|
||||||
unsigned = legacyMxDeviceInfo.unsigned,
|
|
||||||
verified = legacyMxDeviceInfo.mVerified
|
|
||||||
)
|
|
||||||
|
|
||||||
obj.setString("deviceInfoData", serializeForRealm(newMxDeviceInfo))
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert MXOlmInboundGroupSession2 to OlmInboundGroupSessionWrapper
|
|
||||||
realm.schema.get("OlmInboundGroupSessionEntity")
|
|
||||||
?.transform { obj ->
|
|
||||||
try {
|
|
||||||
val oldSerializedData = obj.getString("olmInboundGroupSessionData")
|
|
||||||
deserializeFromRealm<MXOlmInboundGroupSession2>(oldSerializedData)?.let { mxOlmInboundGroupSession2 ->
|
|
||||||
val sessionKey = mxOlmInboundGroupSession2.mSession.sessionIdentifier()
|
|
||||||
val newOlmInboundGroupSessionWrapper = OlmInboundGroupSessionWrapper(sessionKey, false)
|
|
||||||
.apply {
|
|
||||||
olmInboundGroupSession = mxOlmInboundGroupSession2.mSession
|
|
||||||
roomId = mxOlmInboundGroupSession2.mRoomId
|
|
||||||
senderKey = mxOlmInboundGroupSession2.mSenderKey
|
|
||||||
keysClaimed = mxOlmInboundGroupSession2.mKeysClaimed
|
|
||||||
forwardingCurve25519KeyChain = mxOlmInboundGroupSession2.mForwardingCurve25519KeyChain
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.setString("olmInboundGroupSessionData", serializeForRealm(newOlmInboundGroupSessionWrapper))
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Timber.e(e, "Error")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,143 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import com.squareup.moshi.Types
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.MXDeviceInfo
|
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.di.SerializeNulls
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
// Version 4L added Cross Signing info persistence
|
|
||||||
internal class MigrateCryptoTo004(realm: DynamicRealm) : RealmMigrator(realm, 4) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
if (realm.schema.contains("TrustLevelEntity")) {
|
|
||||||
Timber.d("Skipping Step 3 -> 4 because entities already exist")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
Timber.d("Create KeyInfoEntity")
|
|
||||||
val trustLevelEntityEntitySchema = realm.schema.create("TrustLevelEntity")
|
|
||||||
.addField(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED, Boolean::class.java)
|
|
||||||
.setNullable(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED, true)
|
|
||||||
.addField(TrustLevelEntityFields.LOCALLY_VERIFIED, Boolean::class.java)
|
|
||||||
.setNullable(TrustLevelEntityFields.LOCALLY_VERIFIED, true)
|
|
||||||
|
|
||||||
val keyInfoEntitySchema = realm.schema.create("KeyInfoEntity")
|
|
||||||
.addField(KeyInfoEntityFields.PUBLIC_KEY_BASE64, String::class.java)
|
|
||||||
.addField(KeyInfoEntityFields.SIGNATURES, String::class.java)
|
|
||||||
.addRealmListField(KeyInfoEntityFields.USAGES.`$`, String::class.java)
|
|
||||||
.addRealmObjectField(KeyInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevelEntityEntitySchema)
|
|
||||||
|
|
||||||
Timber.d("Create CrossSigningInfoEntity")
|
|
||||||
|
|
||||||
val crossSigningInfoSchema = realm.schema.create("CrossSigningInfoEntity")
|
|
||||||
.addField(CrossSigningInfoEntityFields.USER_ID, String::class.java)
|
|
||||||
.addPrimaryKey(CrossSigningInfoEntityFields.USER_ID)
|
|
||||||
.addRealmListField(CrossSigningInfoEntityFields.CROSS_SIGNING_KEYS.`$`, keyInfoEntitySchema)
|
|
||||||
|
|
||||||
Timber.d("Updating UserEntity table")
|
|
||||||
realm.schema.get("UserEntity")
|
|
||||||
?.addRealmObjectField(UserEntityFields.CROSS_SIGNING_INFO_ENTITY.`$`, crossSigningInfoSchema)
|
|
||||||
|
|
||||||
Timber.d("Updating CryptoMetadataEntity table")
|
|
||||||
realm.schema.get("CryptoMetadataEntity")
|
|
||||||
?.addField(CryptoMetadataEntityFields.X_SIGN_MASTER_PRIVATE_KEY, String::class.java)
|
|
||||||
?.addField(CryptoMetadataEntityFields.X_SIGN_USER_PRIVATE_KEY, String::class.java)
|
|
||||||
?.addField(CryptoMetadataEntityFields.X_SIGN_SELF_SIGNED_PRIVATE_KEY, String::class.java)
|
|
||||||
|
|
||||||
val moshi = Moshi.Builder().add(SerializeNulls.JSON_ADAPTER_FACTORY).build()
|
|
||||||
val listMigrationAdapter = moshi.adapter<List<String>>(
|
|
||||||
Types.newParameterizedType(
|
|
||||||
List::class.java,
|
|
||||||
String::class.java,
|
|
||||||
Any::class.java
|
|
||||||
)
|
|
||||||
)
|
|
||||||
val mapMigrationAdapter = moshi.adapter<JsonDict>(
|
|
||||||
Types.newParameterizedType(
|
|
||||||
Map::class.java,
|
|
||||||
String::class.java,
|
|
||||||
Any::class.java
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
realm.schema.get("DeviceInfoEntity")
|
|
||||||
?.addField(DeviceInfoEntityFields.USER_ID, String::class.java)
|
|
||||||
?.addField(DeviceInfoEntityFields.ALGORITHM_LIST_JSON, String::class.java)
|
|
||||||
?.addField(DeviceInfoEntityFields.KEYS_MAP_JSON, String::class.java)
|
|
||||||
?.addField(DeviceInfoEntityFields.SIGNATURE_MAP_JSON, String::class.java)
|
|
||||||
?.addField(DeviceInfoEntityFields.UNSIGNED_MAP_JSON, String::class.java)
|
|
||||||
?.addField(DeviceInfoEntityFields.IS_BLOCKED, Boolean::class.java)
|
|
||||||
?.setNullable(DeviceInfoEntityFields.IS_BLOCKED, true)
|
|
||||||
?.addRealmObjectField(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevelEntityEntitySchema)
|
|
||||||
?.transform { obj ->
|
|
||||||
|
|
||||||
try {
|
|
||||||
val oldSerializedData = obj.getString("deviceInfoData")
|
|
||||||
deserializeFromRealm<MXDeviceInfo>(oldSerializedData)?.let { oldDevice ->
|
|
||||||
|
|
||||||
val trustLevel = realm.createObject("TrustLevelEntity")
|
|
||||||
when (oldDevice.verified) {
|
|
||||||
MXDeviceInfo.DEVICE_VERIFICATION_UNKNOWN -> {
|
|
||||||
obj.setNull(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`)
|
|
||||||
}
|
|
||||||
MXDeviceInfo.DEVICE_VERIFICATION_BLOCKED -> {
|
|
||||||
trustLevel.setNull(TrustLevelEntityFields.LOCALLY_VERIFIED)
|
|
||||||
trustLevel.setNull(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED)
|
|
||||||
obj.setBoolean(DeviceInfoEntityFields.IS_BLOCKED, oldDevice.isBlocked)
|
|
||||||
obj.setObject(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevel)
|
|
||||||
}
|
|
||||||
MXDeviceInfo.DEVICE_VERIFICATION_UNVERIFIED -> {
|
|
||||||
trustLevel.setBoolean(TrustLevelEntityFields.LOCALLY_VERIFIED, false)
|
|
||||||
trustLevel.setBoolean(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED, false)
|
|
||||||
obj.setObject(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevel)
|
|
||||||
}
|
|
||||||
MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED -> {
|
|
||||||
trustLevel.setBoolean(TrustLevelEntityFields.LOCALLY_VERIFIED, true)
|
|
||||||
trustLevel.setBoolean(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED, false)
|
|
||||||
obj.setObject(DeviceInfoEntityFields.TRUST_LEVEL_ENTITY.`$`, trustLevel)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
obj.setString(DeviceInfoEntityFields.USER_ID, oldDevice.userId)
|
|
||||||
obj.setString(DeviceInfoEntityFields.IDENTITY_KEY, oldDevice.identityKey())
|
|
||||||
obj.setString(DeviceInfoEntityFields.ALGORITHM_LIST_JSON, listMigrationAdapter.toJson(oldDevice.algorithms))
|
|
||||||
obj.setString(DeviceInfoEntityFields.KEYS_MAP_JSON, mapMigrationAdapter.toJson(oldDevice.keys))
|
|
||||||
obj.setString(DeviceInfoEntityFields.SIGNATURE_MAP_JSON, mapMigrationAdapter.toJson(oldDevice.signatures))
|
|
||||||
obj.setString(DeviceInfoEntityFields.UNSIGNED_MAP_JSON, mapMigrationAdapter.toJson(oldDevice.unsigned))
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.w(failure, "Crypto Data base migration error")
|
|
||||||
// an unfortunate refactor did modify that class, making deserialization failing
|
|
||||||
// so we just skip and ignore..
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?.removeField("deviceInfoData")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,62 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
internal class MigrateCryptoTo005(realm: DynamicRealm) : RealmMigrator(realm, 5) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.remove("OutgoingRoomKeyRequestEntity")
|
|
||||||
realm.schema.remove("IncomingRoomKeyRequestEntity")
|
|
||||||
|
|
||||||
// Not need to migrate existing request, just start fresh?
|
|
||||||
realm.schema.create("GossipingEventEntity")
|
|
||||||
.addField("type", String::class.java)
|
|
||||||
.addIndex("type")
|
|
||||||
.addField("content", String::class.java)
|
|
||||||
.addField("sender", String::class.java)
|
|
||||||
.addIndex("sender")
|
|
||||||
.addField("decryptionResultJson", String::class.java)
|
|
||||||
.addField("decryptionErrorCode", String::class.java)
|
|
||||||
.addField("ageLocalTs", Long::class.java)
|
|
||||||
.setNullable("ageLocalTs", true)
|
|
||||||
.addField("sendStateStr", String::class.java)
|
|
||||||
|
|
||||||
realm.schema.create("IncomingGossipingRequestEntity")
|
|
||||||
.addField("requestId", String::class.java)
|
|
||||||
.addIndex("requestId")
|
|
||||||
.addField("typeStr", String::class.java)
|
|
||||||
.addIndex("typeStr")
|
|
||||||
.addField("otherUserId", String::class.java)
|
|
||||||
.addField("requestedInfoStr", String::class.java)
|
|
||||||
.addField("otherDeviceId", String::class.java)
|
|
||||||
.addField("requestStateStr", String::class.java)
|
|
||||||
.addField("localCreationTimestamp", Long::class.java)
|
|
||||||
.setNullable("localCreationTimestamp", true)
|
|
||||||
|
|
||||||
realm.schema.create("OutgoingGossipingRequestEntity")
|
|
||||||
.addField("requestId", String::class.java)
|
|
||||||
.addIndex("requestId")
|
|
||||||
.addField("recipientsData", String::class.java)
|
|
||||||
.addField("requestedInfoStr", String::class.java)
|
|
||||||
.addField("typeStr", String::class.java)
|
|
||||||
.addIndex("typeStr")
|
|
||||||
.addField("requestStateStr", String::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal class MigrateCryptoTo006(realm: DynamicRealm) : RealmMigrator(realm, 6) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
Timber.d("Updating CryptoMetadataEntity table")
|
|
||||||
realm.schema.get("CryptoMetadataEntity")
|
|
||||||
?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY, String::class.java)
|
|
||||||
?.addField(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY_VERSION, String::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CrossSigningKeysMapper
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal class MigrateCryptoTo007(realm: DynamicRealm) : RealmMigrator(realm, 7) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
Timber.d("Updating KeyInfoEntity table")
|
|
||||||
val crossSigningKeysMapper = CrossSigningKeysMapper(MoshiProvider.providesMoshi())
|
|
||||||
|
|
||||||
val keyInfoEntities = realm.where("KeyInfoEntity").findAll()
|
|
||||||
try {
|
|
||||||
keyInfoEntities.forEach {
|
|
||||||
val stringSignatures = it.getString(KeyInfoEntityFields.SIGNATURES)
|
|
||||||
val objectSignatures: Map<String, Map<String, String>>? = deserializeFromRealm(stringSignatures)
|
|
||||||
val jsonSignatures = crossSigningKeysMapper.serializeSignatures(objectSignatures)
|
|
||||||
it.setString(KeyInfoEntityFields.SIGNATURES, jsonSignatures)
|
|
||||||
}
|
|
||||||
} catch (ignore: Throwable) {
|
|
||||||
}
|
|
||||||
|
|
||||||
// Migrate frozen classes
|
|
||||||
val inboundGroupSessions = realm.where("OlmInboundGroupSessionEntity").findAll()
|
|
||||||
inboundGroupSessions.forEach { dynamicObject ->
|
|
||||||
dynamicObject.getString(OlmInboundGroupSessionEntityFields.OLM_INBOUND_GROUP_SESSION_DATA)?.let { serializedObject ->
|
|
||||||
try {
|
|
||||||
deserializeFromRealm<OlmInboundGroupSessionWrapper?>(serializedObject)?.let { oldFormat ->
|
|
||||||
val newFormat = oldFormat.exportKeys()?.let {
|
|
||||||
OlmInboundGroupSessionWrapper2(it)
|
|
||||||
}
|
|
||||||
dynamicObject.setString(OlmInboundGroupSessionEntityFields.OLM_INBOUND_GROUP_SESSION_DATA, serializeForRealm(newFormat))
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure, "## OlmInboundGroupSessionEntity migration failed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
|
||||||
|
|
||||||
internal class MigrateCryptoTo008(
|
|
||||||
realm: DynamicRealm,
|
|
||||||
private val clock: Clock,
|
|
||||||
) : RealmMigrator(realm, 8) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.create("MyDeviceLastSeenInfoEntity")
|
|
||||||
.addField(MyDeviceLastSeenInfoEntityFields.DEVICE_ID, String::class.java)
|
|
||||||
.addPrimaryKey(MyDeviceLastSeenInfoEntityFields.DEVICE_ID)
|
|
||||||
.addField(MyDeviceLastSeenInfoEntityFields.DISPLAY_NAME, String::class.java)
|
|
||||||
.addField(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_IP, String::class.java)
|
|
||||||
.addField(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_TS, Long::class.java)
|
|
||||||
.setNullable(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_TS, true)
|
|
||||||
|
|
||||||
val now = clock.epochMillis()
|
|
||||||
realm.schema.get("DeviceInfoEntity")
|
|
||||||
?.addField(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, Long::class.java)
|
|
||||||
?.setNullable(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, true)
|
|
||||||
?.transform { deviceInfoEntity ->
|
|
||||||
tryOrNull {
|
|
||||||
deviceInfoEntity.setLong(DeviceInfoEntityFields.FIRST_TIME_SEEN_LOCAL_TS, now)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
// Fixes duplicate devices in UserEntity#devices
|
|
||||||
internal class MigrateCryptoTo009(realm: DynamicRealm) : RealmMigrator(realm, 9) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
val userEntities = realm.where("UserEntity").findAll()
|
|
||||||
userEntities.forEach {
|
|
||||||
try {
|
|
||||||
val deviceList = it.getList(UserEntityFields.DEVICES.`$`)
|
|
||||||
?: return@forEach
|
|
||||||
val distinct = deviceList.distinctBy { it.getString(DeviceInfoEntityFields.DEVICE_ID) }
|
|
||||||
if (distinct.size != deviceList.size) {
|
|
||||||
deviceList.clear()
|
|
||||||
deviceList.addAll(distinct)
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.w(failure, "Crypto Data base migration error for migrateTo9")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
// Version 10L added WithHeld Keys Info (MSC2399)
|
|
||||||
internal class MigrateCryptoTo010(realm: DynamicRealm) : RealmMigrator(realm, 10) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.create("WithHeldSessionEntity")
|
|
||||||
.addField(WithHeldSessionEntityFields.ROOM_ID, String::class.java)
|
|
||||||
.addField(WithHeldSessionEntityFields.ALGORITHM, String::class.java)
|
|
||||||
.addField(WithHeldSessionEntityFields.SESSION_ID, String::class.java)
|
|
||||||
.addIndex(WithHeldSessionEntityFields.SESSION_ID)
|
|
||||||
.addField(WithHeldSessionEntityFields.SENDER_KEY, String::class.java)
|
|
||||||
.addIndex(WithHeldSessionEntityFields.SENDER_KEY)
|
|
||||||
.addField(WithHeldSessionEntityFields.CODE_STRING, String::class.java)
|
|
||||||
.addField(WithHeldSessionEntityFields.REASON, String::class.java)
|
|
||||||
|
|
||||||
realm.schema.create("SharedSessionEntity")
|
|
||||||
.addField(SharedSessionEntityFields.ROOM_ID, String::class.java)
|
|
||||||
.addField(SharedSessionEntityFields.ALGORITHM, String::class.java)
|
|
||||||
.addField(SharedSessionEntityFields.SESSION_ID, String::class.java)
|
|
||||||
.addIndex(SharedSessionEntityFields.SESSION_ID)
|
|
||||||
.addField(SharedSessionEntityFields.USER_ID, String::class.java)
|
|
||||||
.addIndex(SharedSessionEntityFields.USER_ID)
|
|
||||||
.addField(SharedSessionEntityFields.DEVICE_ID, String::class.java)
|
|
||||||
.addIndex(SharedSessionEntityFields.DEVICE_ID)
|
|
||||||
.addField(SharedSessionEntityFields.CHAIN_INDEX, Long::class.java)
|
|
||||||
.setNullable(SharedSessionEntityFields.CHAIN_INDEX, true)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
// Version 11L added deviceKeysSentToServer boolean to CryptoMetadataEntity
|
|
||||||
internal class MigrateCryptoTo011(realm: DynamicRealm) : RealmMigrator(realm, 11) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("CryptoMetadataEntity")
|
|
||||||
?.addField(CryptoMetadataEntityFields.DEVICE_KEYS_SENT_TO_SERVER, Boolean::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
// Version 12L added outbound group session persistence
|
|
||||||
internal class MigrateCryptoTo012(realm: DynamicRealm) : RealmMigrator(realm, 12) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
val outboundEntitySchema = realm.schema.create("OutboundGroupSessionInfoEntity")
|
|
||||||
.addField(OutboundGroupSessionInfoEntityFields.SERIALIZED_OUTBOUND_SESSION_DATA, String::class.java)
|
|
||||||
.addField(OutboundGroupSessionInfoEntityFields.CREATION_TIME, Long::class.java)
|
|
||||||
.setNullable(OutboundGroupSessionInfoEntityFields.CREATION_TIME, true)
|
|
||||||
|
|
||||||
realm.schema.get("CryptoRoomEntity")
|
|
||||||
?.addRealmObjectField(CryptoRoomEntityFields.OUTBOUND_SESSION_INFO.`$`, outboundEntitySchema)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
// Version 13L delete unreferenced TrustLevelEntity
|
|
||||||
internal class MigrateCryptoTo013(realm: DynamicRealm) : RealmMigrator(realm, 13) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
// Use a trick to do that... Ref: https://stackoverflow.com/questions/55221366
|
|
||||||
val trustLevelEntitySchema = realm.schema.get("TrustLevelEntity")
|
|
||||||
|
|
||||||
/*
|
|
||||||
Creating a new temp field called isLinked which is set to true for those which are
|
|
||||||
references by other objects. Rest of them are set to false. Then removing all
|
|
||||||
those which are false and hence duplicate and unnecessary. Then removing the temp field
|
|
||||||
isLinked
|
|
||||||
*/
|
|
||||||
var mainCounter = 0
|
|
||||||
var deviceInfoCounter = 0
|
|
||||||
var keyInfoCounter = 0
|
|
||||||
val deleteCounter: Int
|
|
||||||
|
|
||||||
trustLevelEntitySchema
|
|
||||||
?.addField("isLinked", Boolean::class.java)
|
|
||||||
?.transform { obj ->
|
|
||||||
// Setting to false for all by default
|
|
||||||
obj.set("isLinked", false)
|
|
||||||
mainCounter++
|
|
||||||
}
|
|
||||||
|
|
||||||
realm.schema.get("DeviceInfoEntity")?.transform { obj ->
|
|
||||||
// Setting to true for those which are referenced in DeviceInfoEntity
|
|
||||||
deviceInfoCounter++
|
|
||||||
obj.getObject("trustLevelEntity")?.set("isLinked", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
realm.schema.get("KeyInfoEntity")?.transform { obj ->
|
|
||||||
// Setting to true for those which are referenced in KeyInfoEntity
|
|
||||||
keyInfoCounter++
|
|
||||||
obj.getObject("trustLevelEntity")?.set("isLinked", true)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removing all those which are set as false
|
|
||||||
realm.where("TrustLevelEntity")
|
|
||||||
.equalTo("isLinked", false)
|
|
||||||
.findAll()
|
|
||||||
.also { deleteCounter = it.size }
|
|
||||||
.deleteAllFromRealm()
|
|
||||||
|
|
||||||
trustLevelEntitySchema?.removeField("isLinked")
|
|
||||||
|
|
||||||
Timber.w("TrustLevelEntity cleanup: $mainCounter entities")
|
|
||||||
Timber.w("TrustLevelEntity cleanup: $deviceInfoCounter entities referenced in DeviceInfoEntities")
|
|
||||||
Timber.w("TrustLevelEntity cleanup: $keyInfoCounter entities referenced in KeyInfoEntity")
|
|
||||||
Timber.w("TrustLevelEntity cleanup: $deleteCounter entities deleted!")
|
|
||||||
if (mainCounter != deviceInfoCounter + keyInfoCounter + deleteCounter) {
|
|
||||||
Timber.e("TrustLevelEntity cleanup: Something is not correct...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
// Version 14L Update the way we remember key sharing
|
|
||||||
internal class MigrateCryptoTo014(realm: DynamicRealm) : RealmMigrator(realm, 14) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("SharedSessionEntity")
|
|
||||||
?.addField(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, String::class.java)
|
|
||||||
?.addIndex(SharedSessionEntityFields.DEVICE_IDENTITY_KEY)
|
|
||||||
?.transform {
|
|
||||||
val sharedUserId = it.getString(SharedSessionEntityFields.USER_ID)
|
|
||||||
val sharedDeviceId = it.getString(SharedSessionEntityFields.DEVICE_ID)
|
|
||||||
val knownDevice = realm.where("DeviceInfoEntity")
|
|
||||||
.equalTo(DeviceInfoEntityFields.USER_ID, sharedUserId)
|
|
||||||
.equalTo(DeviceInfoEntityFields.DEVICE_ID, sharedDeviceId)
|
|
||||||
.findFirst()
|
|
||||||
it.setString(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, knownDevice?.getString(DeviceInfoEntityFields.IDENTITY_KEY))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
// Version 15L adds wasEncryptedOnce field to CryptoRoomEntity
|
|
||||||
internal class MigrateCryptoTo015(realm: DynamicRealm) : RealmMigrator(realm, 15) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("CryptoRoomEntity")
|
|
||||||
?.addField(CryptoRoomEntityFields.WAS_ENCRYPTED_ONCE, Boolean::class.java)
|
|
||||||
?.setNullable(CryptoRoomEntityFields.WAS_ENCRYPTED_ONCE, true)
|
|
||||||
?.transform {
|
|
||||||
val currentAlgorithm = it.getString(CryptoRoomEntityFields.ALGORITHM)
|
|
||||||
it.set(CryptoRoomEntityFields.WAS_ENCRYPTED_ONCE, currentAlgorithm == MXCRYPTO_ALGORITHM_MEGOLM)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.AuditTrailEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyRequestReplyEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
internal class MigrateCryptoTo016(realm: DynamicRealm) : RealmMigrator(realm, 16) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.remove("OutgoingGossipingRequestEntity")
|
|
||||||
realm.schema.remove("IncomingGossipingRequestEntity")
|
|
||||||
realm.schema.remove("GossipingEventEntity")
|
|
||||||
|
|
||||||
// No need to migrate existing request, just start fresh
|
|
||||||
|
|
||||||
val replySchema = realm.schema.create("KeyRequestReplyEntity")
|
|
||||||
.addField(KeyRequestReplyEntityFields.SENDER_ID, String::class.java)
|
|
||||||
.addField(KeyRequestReplyEntityFields.FROM_DEVICE, String::class.java)
|
|
||||||
.addField(KeyRequestReplyEntityFields.EVENT_JSON, String::class.java)
|
|
||||||
|
|
||||||
realm.schema.create("OutgoingKeyRequestEntity")
|
|
||||||
.addField(OutgoingKeyRequestEntityFields.REQUEST_ID, String::class.java)
|
|
||||||
.addIndex(OutgoingKeyRequestEntityFields.REQUEST_ID)
|
|
||||||
.addField(OutgoingKeyRequestEntityFields.MEGOLM_SESSION_ID, String::class.java)
|
|
||||||
.addIndex(OutgoingKeyRequestEntityFields.MEGOLM_SESSION_ID)
|
|
||||||
.addRealmListField(OutgoingKeyRequestEntityFields.REPLIES.`$`, replySchema)
|
|
||||||
.addField(OutgoingKeyRequestEntityFields.RECIPIENTS_DATA, String::class.java)
|
|
||||||
.addField(OutgoingKeyRequestEntityFields.REQUEST_STATE_STR, String::class.java)
|
|
||||||
.addIndex(OutgoingKeyRequestEntityFields.REQUEST_STATE_STR)
|
|
||||||
.addField(OutgoingKeyRequestEntityFields.REQUESTED_INFO_STR, String::class.java)
|
|
||||||
.addField(OutgoingKeyRequestEntityFields.ROOM_ID, String::class.java)
|
|
||||||
.addIndex(OutgoingKeyRequestEntityFields.ROOM_ID)
|
|
||||||
.addField(OutgoingKeyRequestEntityFields.REQUESTED_INDEX, Integer::class.java)
|
|
||||||
.addField(OutgoingKeyRequestEntityFields.CREATION_TIME_STAMP, Long::class.java)
|
|
||||||
.setNullable(OutgoingKeyRequestEntityFields.CREATION_TIME_STAMP, true)
|
|
||||||
|
|
||||||
realm.schema.create("AuditTrailEntity")
|
|
||||||
.addField(AuditTrailEntityFields.AGE_LOCAL_TS, Long::class.java)
|
|
||||||
.setNullable(AuditTrailEntityFields.AGE_LOCAL_TS, true)
|
|
||||||
.addField(AuditTrailEntityFields.CONTENT_JSON, String::class.java)
|
|
||||||
.addField(AuditTrailEntityFields.TYPE, String::class.java)
|
|
||||||
.addIndex(AuditTrailEntityFields.TYPE)
|
|
||||||
|
|
||||||
realm.schema.get("CryptoMetadataEntity")
|
|
||||||
?.addField(CryptoMetadataEntityFields.GLOBAL_ENABLE_KEY_GOSSIPING, Boolean::class.java)
|
|
||||||
?.transform {
|
|
||||||
// set the default value to true
|
|
||||||
it.setBoolean(CryptoMetadataEntityFields.GLOBAL_ENABLE_KEY_GOSSIPING, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Version 17L enhance OlmInboundGroupSessionEntity to support shared history for MSC3061.
|
|
||||||
* Also migrates how megolm session are stored to avoid additional serialized frozen class.
|
|
||||||
*/
|
|
||||||
internal class MigrateCryptoTo017(realm: DynamicRealm) : RealmMigrator(realm, 17) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("CryptoRoomEntity")
|
|
||||||
?.addField(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, Boolean::class.java)?.transform {
|
|
||||||
// We don't have access to the session database to check for the state here and set the good value.
|
|
||||||
// But for now as it's behind a lab flag, will set to false and force initial sync when enabled
|
|
||||||
it.setBoolean(CryptoRoomEntityFields.SHOULD_SHARE_HISTORY, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
realm.schema.get("OutboundGroupSessionInfoEntity")
|
|
||||||
?.addField(OutboundGroupSessionInfoEntityFields.SHOULD_SHARE_HISTORY, Boolean::class.java)?.transform {
|
|
||||||
// We don't have access to the session database to check for the state here and set the good value.
|
|
||||||
// But for now as it's behind a lab flag, will set to false and force initial sync when enabled
|
|
||||||
it.setBoolean(OutboundGroupSessionInfoEntityFields.SHOULD_SHARE_HISTORY, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
realm.schema.get("CryptoMetadataEntity")
|
|
||||||
?.addField(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, Boolean::class.java)
|
|
||||||
?.transform { obj ->
|
|
||||||
// default to false
|
|
||||||
obj.setBoolean(CryptoMetadataEntityFields.ENABLE_KEY_FORWARDING_ON_INVITE, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
val moshiAdapter = MoshiProvider.providesMoshi().adapter(InboundGroupSessionData::class.java)
|
|
||||||
|
|
||||||
realm.schema.get("OlmInboundGroupSessionEntity")
|
|
||||||
?.addField(OlmInboundGroupSessionEntityFields.SHARED_HISTORY, Boolean::class.java)
|
|
||||||
?.addField(OlmInboundGroupSessionEntityFields.ROOM_ID, String::class.java)
|
|
||||||
?.addField(OlmInboundGroupSessionEntityFields.INBOUND_GROUP_SESSION_DATA_JSON, String::class.java)
|
|
||||||
?.addField(OlmInboundGroupSessionEntityFields.SERIALIZED_OLM_INBOUND_GROUP_SESSION, String::class.java)
|
|
||||||
?.transform { dynamicObject ->
|
|
||||||
try {
|
|
||||||
// we want to convert the old wrapper frozen class into a
|
|
||||||
// map of sessionData & the pickled session herself
|
|
||||||
dynamicObject.getString(OlmInboundGroupSessionEntityFields.OLM_INBOUND_GROUP_SESSION_DATA)?.let { oldData ->
|
|
||||||
val oldWrapper = tryOrNull("Failed to convert megolm inbound group data") {
|
|
||||||
@Suppress("DEPRECATION")
|
|
||||||
deserializeFromRealm<org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2?>(oldData)
|
|
||||||
}
|
|
||||||
val groupSession = oldWrapper?.olmInboundGroupSession
|
|
||||||
?: return@transform Unit.also {
|
|
||||||
Timber.w("Failed to migrate megolm session, no olmInboundGroupSession")
|
|
||||||
}
|
|
||||||
// now convert to new data
|
|
||||||
val data = InboundGroupSessionData(
|
|
||||||
senderKey = oldWrapper.senderKey,
|
|
||||||
roomId = oldWrapper.roomId,
|
|
||||||
keysClaimed = oldWrapper.keysClaimed,
|
|
||||||
forwardingCurve25519KeyChain = oldWrapper.forwardingCurve25519KeyChain,
|
|
||||||
sharedHistory = false,
|
|
||||||
)
|
|
||||||
|
|
||||||
dynamicObject.setString(OlmInboundGroupSessionEntityFields.INBOUND_GROUP_SESSION_DATA_JSON, moshiAdapter.toJson(data))
|
|
||||||
dynamicObject.setString(OlmInboundGroupSessionEntityFields.SERIALIZED_OLM_INBOUND_GROUP_SESSION, serializeForRealm(groupSession))
|
|
||||||
|
|
||||||
// denormalized fields
|
|
||||||
dynamicObject.setString(OlmInboundGroupSessionEntityFields.ROOM_ID, oldWrapper.roomId)
|
|
||||||
dynamicObject.setBoolean(OlmInboundGroupSessionEntityFields.SHARED_HISTORY, false)
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure, "Failed to migrate megolm session")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This migration is adding support for trusted flags on megolm sessions.
|
|
||||||
* We can't really assert the trust of existing keys, so for the sake of simplicity we are going to
|
|
||||||
* mark existing keys as safe.
|
|
||||||
* This migration can take long depending on the account
|
|
||||||
*/
|
|
||||||
internal class MigrateCryptoTo018(realm: DynamicRealm) : RealmMigrator(realm, 18) {
|
|
||||||
|
|
||||||
private val moshiAdapter = MoshiProvider.providesMoshi().adapter(InboundGroupSessionData::class.java)
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("OlmInboundGroupSessionEntity")
|
|
||||||
?.transform { dynamicObject ->
|
|
||||||
try {
|
|
||||||
dynamicObject.getString(OlmInboundGroupSessionEntityFields.INBOUND_GROUP_SESSION_DATA_JSON)?.let { oldData ->
|
|
||||||
moshiAdapter.fromJson(oldData)?.let { dataToMigrate ->
|
|
||||||
dataToMigrate.copy(trusted = true).let {
|
|
||||||
dynamicObject.setString(OlmInboundGroupSessionEntityFields.INBOUND_GROUP_SESSION_DATA_JSON, moshiAdapter.toJson(it))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure, "Failed to migrate megolm session")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import io.realm.DynamicRealmObject
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.KeyUsage
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This migration is adding support for trusted flags on megolm sessions.
|
|
||||||
* We can't really assert the trust of existing keys, so for the sake of simplicity we are going to
|
|
||||||
* mark existing keys as safe.
|
|
||||||
* This migration can take long depending on the account
|
|
||||||
*/
|
|
||||||
internal class MigrateCryptoTo019(realm: DynamicRealm) : RealmMigrator(realm, 19) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("CrossSigningInfoEntity")
|
|
||||||
?.addField(CrossSigningInfoEntityFields.WAS_USER_VERIFIED_ONCE, Boolean::class.java)
|
|
||||||
?.transform { dynamicObject ->
|
|
||||||
|
|
||||||
val knowKeys = dynamicObject.getList(CrossSigningInfoEntityFields.CROSS_SIGNING_KEYS.`$`)
|
|
||||||
val msk = knowKeys.firstOrNull {
|
|
||||||
it.getList(KeyInfoEntityFields.USAGES.`$`, String::class.java).orEmpty().contains(KeyUsage.MASTER.value)
|
|
||||||
}
|
|
||||||
val ssk = knowKeys.firstOrNull {
|
|
||||||
it.getList(KeyInfoEntityFields.USAGES.`$`, String::class.java).orEmpty().contains(KeyUsage.SELF_SIGNING.value)
|
|
||||||
}
|
|
||||||
val isTrusted = isDynamicKeyInfoTrusted(msk?.get<DynamicRealmObject>(KeyInfoEntityFields.TRUST_LEVEL_ENTITY.`$`)) &&
|
|
||||||
isDynamicKeyInfoTrusted(ssk?.get<DynamicRealmObject>(KeyInfoEntityFields.TRUST_LEVEL_ENTITY.`$`))
|
|
||||||
|
|
||||||
dynamicObject.setBoolean(CrossSigningInfoEntityFields.WAS_USER_VERIFIED_ONCE, isTrusted)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isDynamicKeyInfoTrusted(keyInfo: DynamicRealmObject?): Boolean {
|
|
||||||
if (keyInfo == null) return false
|
|
||||||
return !keyInfo.isNull(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED) && keyInfo.getBoolean(TrustLevelEntityFields.CROSS_SIGNED_VERIFIED) &&
|
|
||||||
!keyInfo.isNull(TrustLevelEntityFields.LOCALLY_VERIFIED) && keyInfo.getBoolean(TrustLevelEntityFields.LOCALLY_VERIFIED)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,32 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This migration adds a new field into MyDeviceLastSeenInfoEntity corresponding to the last seen user agent.
|
|
||||||
*/
|
|
||||||
internal class MigrateCryptoTo020(realm: DynamicRealm) : RealmMigrator(realm, 20) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("MyDeviceLastSeenInfoEntity")
|
|
||||||
?.addField(MyDeviceLastSeenInfoEntityFields.LAST_SEEN_USER_AGENT, String::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.api.crypto.MEGOLM_DEFAULT_ROTATION_MSGS
|
|
||||||
import org.matrix.android.sdk.api.crypto.MEGOLM_DEFAULT_ROTATION_PERIOD_MS
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This migration stores the rotation parameters for megolm oubound sessions.
|
|
||||||
*/
|
|
||||||
internal class MigrateCryptoTo021(realm: DynamicRealm) : RealmMigrator(realm, 21) {
|
|
||||||
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("CryptoRoomEntity")
|
|
||||||
?.addField(CryptoRoomEntityFields.ROTATION_PERIOD_MS, Long::class.java)
|
|
||||||
?.setNullable(CryptoRoomEntityFields.ROTATION_PERIOD_MS, true)
|
|
||||||
?.addField(CryptoRoomEntityFields.ROTATION_PERIOD_MSGS, Long::class.java)
|
|
||||||
?.setNullable(CryptoRoomEntityFields.ROTATION_PERIOD_MSGS, true)
|
|
||||||
?.transform {
|
|
||||||
// As a migration we set the default (will be on par with existing code)
|
|
||||||
// A clear cache will have the correct values.
|
|
||||||
it.setLong(CryptoRoomEntityFields.ROTATION_PERIOD_MS, MEGOLM_DEFAULT_ROTATION_PERIOD_MS)
|
|
||||||
it.setLong(CryptoRoomEntityFields.ROTATION_PERIOD_MSGS, MEGOLM_DEFAULT_ROTATION_MSGS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.RustEncryptionConfiguration
|
|
||||||
import org.matrix.android.sdk.internal.session.MigrateEAtoEROperation
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This migration creates the rust database and migrates from legacy crypto.
|
|
||||||
*/
|
|
||||||
internal class MigrateCryptoTo022(
|
|
||||||
realm: DynamicRealm,
|
|
||||||
private val rustDirectory: File,
|
|
||||||
private val rustEncryptionConfiguration: RustEncryptionConfiguration,
|
|
||||||
private val migrateMegolmGroupSessions: Boolean = false
|
|
||||||
) : RealmMigrator(
|
|
||||||
realm,
|
|
||||||
22
|
|
||||||
) {
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
// Migrate to rust!
|
|
||||||
val migrateOperation = MigrateEAtoEROperation(migrateMegolmGroupSessions)
|
|
||||||
migrateOperation.dynamicExecute(realm, rustDirectory, rustEncryptionConfiguration.getDatabasePassphrase())
|
|
||||||
|
|
||||||
// wa can't delete all for now, but we can do some cleaning
|
|
||||||
realm.schema.get("OlmSessionEntity")?.transform {
|
|
||||||
it.deleteFromRealm()
|
|
||||||
}
|
|
||||||
|
|
||||||
// a future migration will clean the rest
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2024 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.util.database.RealmMigrator
|
|
||||||
|
|
||||||
// Some fields are now required due to upgrade of Kotlin version.
|
|
||||||
// See https://github.com/realm/realm-java/issues/7810 for more details.
|
|
||||||
internal class MigrateCryptoTo023(realm: DynamicRealm) : RealmMigrator(realm, 23) {
|
|
||||||
override fun doMigrate(realm: DynamicRealm) {
|
|
||||||
realm.schema.get("OutgoingKeyRequestEntity")
|
|
||||||
?.setRequired(OutgoingKeyRequestEntityFields.REQUEST_STATE_STR, true)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration.rust
|
|
||||||
|
|
||||||
data class ExtractMigrationDataFailure(override val cause: Throwable) :
|
|
||||||
java.lang.RuntimeException("Can't proceed with migration, crypto store is empty or some necessary data is missing.", cause)
|
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration.rust
|
|
||||||
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmConfiguration
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
|
||||||
import org.matrix.olm.OlmUtility
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.MigrationData
|
|
||||||
import timber.log.Timber
|
|
||||||
import kotlin.system.measureTimeMillis
|
|
||||||
|
|
||||||
internal class ExtractMigrationDataUseCase(private val migrateGroupSessions: Boolean = false) {
|
|
||||||
|
|
||||||
fun extractData(realm: RealmToMigrate, importPartial: ((MigrationData) -> Unit)) {
|
|
||||||
return try {
|
|
||||||
extract(realm, importPartial)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
throw ExtractMigrationDataFailure(failure)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun hasExistingData(realmConfiguration: RealmConfiguration): Boolean {
|
|
||||||
return Realm.getInstance(realmConfiguration).use { realm ->
|
|
||||||
!realm.isEmpty &&
|
|
||||||
// Check if there is a MetaData object
|
|
||||||
realm.where<CryptoMetadataEntity>().count() > 0 &&
|
|
||||||
realm.where<CryptoMetadataEntity>().findFirst()?.olmAccountData != null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun extract(realm: RealmToMigrate, importPartial: ((MigrationData) -> Unit)) {
|
|
||||||
val pickleKey = OlmUtility.getRandomKey()
|
|
||||||
|
|
||||||
val baseExtract = realm.getPickledAccount(pickleKey)
|
|
||||||
// import the account asap
|
|
||||||
importPartial(baseExtract)
|
|
||||||
|
|
||||||
val chunkSize = 500
|
|
||||||
realm.trackedUsersChunk(500) {
|
|
||||||
importPartial(
|
|
||||||
baseExtract.copy(trackedUsers = it)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var migratedOlmSessionCount = 0
|
|
||||||
var writeTime = 0L
|
|
||||||
measureTimeMillis {
|
|
||||||
realm.pickledOlmSessions(pickleKey, chunkSize) { pickledSessions ->
|
|
||||||
migratedOlmSessionCount += pickledSessions.size
|
|
||||||
measureTimeMillis {
|
|
||||||
importPartial(
|
|
||||||
baseExtract.copy(sessions = pickledSessions)
|
|
||||||
)
|
|
||||||
}.also { writeTime += it }
|
|
||||||
}
|
|
||||||
}.also {
|
|
||||||
Timber.i("Migration: took $it ms to migrate $migratedOlmSessionCount olm sessions")
|
|
||||||
Timber.i("Migration: rust import time $writeTime")
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't migrate outbound session by default directly after migration
|
|
||||||
// We are going to do it lazyly when decryption fails
|
|
||||||
if (migrateGroupSessions) {
|
|
||||||
var migratedInboundGroupSessionCount = 0
|
|
||||||
measureTimeMillis {
|
|
||||||
realm.pickledOlmGroupSessions(pickleKey, chunkSize) { pickledSessions ->
|
|
||||||
migratedInboundGroupSessionCount += pickledSessions.size
|
|
||||||
measureTimeMillis {
|
|
||||||
importPartial(
|
|
||||||
baseExtract.copy(inboundGroupSessions = pickledSessions)
|
|
||||||
)
|
|
||||||
}.also { writeTime += it }
|
|
||||||
}
|
|
||||||
}.also {
|
|
||||||
Timber.i("Migration: took $it ms to migrate $migratedInboundGroupSessionCount group sessions")
|
|
||||||
Timber.i("Migration: rust import time $writeTime")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,336 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.migration.rust
|
|
||||||
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
import okhttp3.internal.toImmutableList
|
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
|
||||||
import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
|
||||||
import org.matrix.olm.OlmAccount
|
|
||||||
import org.matrix.olm.OlmInboundGroupSession
|
|
||||||
import org.matrix.olm.OlmSession
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.CrossSigningKeyExport
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.MigrationData
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.PickledAccount
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.PickledInboundGroupSession
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.PickledSession
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.nio.charset.Charset
|
|
||||||
|
|
||||||
sealed class RealmToMigrate {
|
|
||||||
data class DynamicRealm(val realm: io.realm.DynamicRealm) : RealmToMigrate()
|
|
||||||
data class ClassicRealm(val realm: io.realm.Realm) : RealmToMigrate()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun RealmToMigrate.hasExistingData(): Boolean {
|
|
||||||
return when (this) {
|
|
||||||
is RealmToMigrate.ClassicRealm -> {
|
|
||||||
!this.realm.isEmpty &&
|
|
||||||
// Check if there is a MetaData object
|
|
||||||
this.realm.where<CryptoMetadataEntity>().count() > 0 &&
|
|
||||||
this.realm.where<CryptoMetadataEntity>().findFirst()?.olmAccountData != null
|
|
||||||
}
|
|
||||||
is RealmToMigrate.DynamicRealm -> {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws
|
|
||||||
fun RealmToMigrate.getPickledAccount(pickleKey: ByteArray): MigrationData {
|
|
||||||
return when (this) {
|
|
||||||
is RealmToMigrate.ClassicRealm -> {
|
|
||||||
val metadataEntity = realm.where<CryptoMetadataEntity>().findFirst()
|
|
||||||
?: throw java.lang.IllegalArgumentException("Rust db migration: No existing metadataEntity")
|
|
||||||
|
|
||||||
val masterKey = metadataEntity.xSignMasterPrivateKey
|
|
||||||
val userKey = metadataEntity.xSignUserPrivateKey
|
|
||||||
val selfSignedKey = metadataEntity.xSignSelfSignedPrivateKey
|
|
||||||
|
|
||||||
Timber.i("## Migration: has private MSK ${masterKey.isNullOrBlank().not()}")
|
|
||||||
Timber.i("## Migration: has private USK ${userKey.isNullOrBlank().not()}")
|
|
||||||
Timber.i("## Migration: has private SSK ${selfSignedKey.isNullOrBlank().not()}")
|
|
||||||
|
|
||||||
val userId = metadataEntity.userId
|
|
||||||
?: throw java.lang.IllegalArgumentException("Rust db migration: userId is null")
|
|
||||||
val deviceId = metadataEntity.deviceId
|
|
||||||
?: throw java.lang.IllegalArgumentException("Rust db migration: deviceID is null")
|
|
||||||
|
|
||||||
val backupVersion = metadataEntity.backupVersion
|
|
||||||
val backupRecoveryKey = metadataEntity.keyBackupRecoveryKey
|
|
||||||
|
|
||||||
Timber.i("## Migration: has private backup key ${backupRecoveryKey != null} for version $backupVersion")
|
|
||||||
|
|
||||||
val isOlmAccountShared = metadataEntity.deviceKeysSentToServer
|
|
||||||
|
|
||||||
val olmAccount = metadataEntity.getOlmAccount()
|
|
||||||
?: throw java.lang.IllegalArgumentException("Rust db migration: No existing account")
|
|
||||||
val pickledOlmAccount = olmAccount.pickle(pickleKey, StringBuffer()).asString()
|
|
||||||
|
|
||||||
val pickledAccount = PickledAccount(
|
|
||||||
userId = userId,
|
|
||||||
deviceId = deviceId,
|
|
||||||
pickle = pickledOlmAccount,
|
|
||||||
shared = isOlmAccountShared,
|
|
||||||
uploadedSignedKeyCount = 50
|
|
||||||
)
|
|
||||||
MigrationData(
|
|
||||||
account = pickledAccount,
|
|
||||||
pickleKey = pickleKey,
|
|
||||||
crossSigning = CrossSigningKeyExport(
|
|
||||||
masterKey = masterKey,
|
|
||||||
selfSigningKey = selfSignedKey,
|
|
||||||
userSigningKey = userKey
|
|
||||||
),
|
|
||||||
sessions = emptyList(),
|
|
||||||
backupRecoveryKey = backupRecoveryKey,
|
|
||||||
trackedUsers = emptyList(),
|
|
||||||
inboundGroupSessions = emptyList(),
|
|
||||||
backupVersion = backupVersion,
|
|
||||||
// TODO import room settings from legacy DB
|
|
||||||
roomSettings = emptyMap()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
is RealmToMigrate.DynamicRealm -> {
|
|
||||||
val cryptoMetadataEntitySchema = realm.schema.get("CryptoMetadataEntity")
|
|
||||||
?: throw java.lang.IllegalStateException("Missing Metadata entity")
|
|
||||||
|
|
||||||
var migrationData: MigrationData? = null
|
|
||||||
cryptoMetadataEntitySchema.transform { dynMetaData ->
|
|
||||||
|
|
||||||
val serializedOlmAccount = dynMetaData.getString(CryptoMetadataEntityFields.OLM_ACCOUNT_DATA)
|
|
||||||
|
|
||||||
val masterKey = dynMetaData.getString(CryptoMetadataEntityFields.X_SIGN_MASTER_PRIVATE_KEY)
|
|
||||||
val userKey = dynMetaData.getString(CryptoMetadataEntityFields.X_SIGN_USER_PRIVATE_KEY)
|
|
||||||
val selfSignedKey = dynMetaData.getString(CryptoMetadataEntityFields.X_SIGN_SELF_SIGNED_PRIVATE_KEY)
|
|
||||||
|
|
||||||
val userId = dynMetaData.getString(CryptoMetadataEntityFields.USER_ID)
|
|
||||||
?: throw java.lang.IllegalArgumentException("Rust db migration: userId is null")
|
|
||||||
val deviceId = dynMetaData.getString(CryptoMetadataEntityFields.DEVICE_ID)
|
|
||||||
?: throw java.lang.IllegalArgumentException("Rust db migration: deviceID is null")
|
|
||||||
|
|
||||||
val backupVersion = dynMetaData.getString(CryptoMetadataEntityFields.BACKUP_VERSION)
|
|
||||||
val backupRecoveryKey = dynMetaData.getString(CryptoMetadataEntityFields.KEY_BACKUP_RECOVERY_KEY)
|
|
||||||
|
|
||||||
val isOlmAccountShared = dynMetaData.getBoolean(CryptoMetadataEntityFields.DEVICE_KEYS_SENT_TO_SERVER)
|
|
||||||
|
|
||||||
val olmAccount = deserializeFromRealm<OlmAccount>(serializedOlmAccount)
|
|
||||||
?: throw java.lang.IllegalArgumentException("Rust db migration: No existing account")
|
|
||||||
|
|
||||||
val pickledOlmAccount = olmAccount.pickle(pickleKey, StringBuffer()).asString()
|
|
||||||
|
|
||||||
val pickledAccount = PickledAccount(
|
|
||||||
userId = userId,
|
|
||||||
deviceId = deviceId,
|
|
||||||
pickle = pickledOlmAccount,
|
|
||||||
shared = isOlmAccountShared,
|
|
||||||
uploadedSignedKeyCount = 50
|
|
||||||
)
|
|
||||||
|
|
||||||
migrationData = MigrationData(
|
|
||||||
account = pickledAccount,
|
|
||||||
pickleKey = pickleKey,
|
|
||||||
crossSigning = CrossSigningKeyExport(
|
|
||||||
masterKey = masterKey,
|
|
||||||
selfSigningKey = selfSignedKey,
|
|
||||||
userSigningKey = userKey
|
|
||||||
),
|
|
||||||
sessions = emptyList(),
|
|
||||||
backupRecoveryKey = backupRecoveryKey,
|
|
||||||
trackedUsers = emptyList(),
|
|
||||||
inboundGroupSessions = emptyList(),
|
|
||||||
backupVersion = backupVersion,
|
|
||||||
// TODO import room settings from legacy DB
|
|
||||||
roomSettings = emptyMap()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
migrationData!!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun RealmToMigrate.trackedUsersChunk(chunkSize: Int, onChunk: ((List<String>) -> Unit)) {
|
|
||||||
when (this) {
|
|
||||||
is RealmToMigrate.ClassicRealm -> {
|
|
||||||
realm.where<UserEntity>()
|
|
||||||
.findAll()
|
|
||||||
.chunked(chunkSize)
|
|
||||||
.onEach {
|
|
||||||
onChunk(it.mapNotNull { it.userId })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RealmToMigrate.DynamicRealm -> {
|
|
||||||
val userList = mutableListOf<String>()
|
|
||||||
realm.schema.get("UserEntity")?.transform {
|
|
||||||
val userId = it.getString(UserEntityFields.USER_ID)
|
|
||||||
// should we check the tracking status?
|
|
||||||
userList.add(userId)
|
|
||||||
if (userList.size > chunkSize) {
|
|
||||||
onChunk(userList.toImmutableList())
|
|
||||||
userList.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (userList.isNotEmpty()) {
|
|
||||||
onChunk(userList)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun RealmToMigrate.pickledOlmSessions(pickleKey: ByteArray, chunkSize: Int, onChunk: ((List<PickledSession>) -> Unit)) {
|
|
||||||
when (this) {
|
|
||||||
is RealmToMigrate.ClassicRealm -> {
|
|
||||||
realm.where<OlmSessionEntity>().findAll()
|
|
||||||
.chunked(chunkSize) { chunk ->
|
|
||||||
val export = chunk.map { it.toPickledSession(pickleKey) }
|
|
||||||
onChunk(export)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RealmToMigrate.DynamicRealm -> {
|
|
||||||
val pickledSessions = mutableListOf<PickledSession>()
|
|
||||||
realm.schema.get("OlmSessionEntity")?.transform {
|
|
||||||
val sessionData = it.getString(OlmSessionEntityFields.OLM_SESSION_DATA)
|
|
||||||
val deviceKey = it.getString(OlmSessionEntityFields.DEVICE_KEY)
|
|
||||||
val lastReceivedMessageTs = it.getLong(OlmSessionEntityFields.LAST_RECEIVED_MESSAGE_TS)
|
|
||||||
val olmSession = deserializeFromRealm<OlmSession>(sessionData)!!
|
|
||||||
val pickle = olmSession.pickle(pickleKey, StringBuffer()).asString()
|
|
||||||
val pickledSession = PickledSession(
|
|
||||||
pickle = pickle,
|
|
||||||
senderKey = deviceKey,
|
|
||||||
createdUsingFallbackKey = false,
|
|
||||||
// / Unix timestamp (in seconds) when the session was created.
|
|
||||||
creationTime = (lastReceivedMessageTs / 1000).toULong(),
|
|
||||||
// / Unix timestamp (in seconds) when the session was last used.
|
|
||||||
lastUseTime = (lastReceivedMessageTs / 1000).toULong(),
|
|
||||||
)
|
|
||||||
// should we check the tracking status?
|
|
||||||
pickledSessions.add(pickledSession)
|
|
||||||
if (pickledSessions.size > chunkSize) {
|
|
||||||
onChunk(pickledSessions.toImmutableList())
|
|
||||||
pickledSessions.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pickledSessions.isNotEmpty()) {
|
|
||||||
onChunk(pickledSessions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val sessionDataAdapter = MoshiProvider.providesMoshi()
|
|
||||||
.adapter(InboundGroupSessionData::class.java)
|
|
||||||
fun RealmToMigrate.pickledOlmGroupSessions(pickleKey: ByteArray, chunkSize: Int, onChunk: ((List<PickledInboundGroupSession>) -> Unit)) {
|
|
||||||
when (this) {
|
|
||||||
is RealmToMigrate.ClassicRealm -> {
|
|
||||||
realm.where<OlmInboundGroupSessionEntity>()
|
|
||||||
.findAll()
|
|
||||||
.chunked(chunkSize) { chunk ->
|
|
||||||
val export = chunk.mapNotNull { it.toPickledInboundGroupSession(pickleKey) }
|
|
||||||
onChunk(export)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RealmToMigrate.DynamicRealm -> {
|
|
||||||
val pickledSessions = mutableListOf<PickledInboundGroupSession>()
|
|
||||||
realm.schema.get("OlmInboundGroupSessionEntity")?.transform {
|
|
||||||
val senderKey = it.getString(OlmInboundGroupSessionEntityFields.SENDER_KEY)
|
|
||||||
val roomId = it.getString(OlmInboundGroupSessionEntityFields.ROOM_ID)
|
|
||||||
val backedUp = it.getBoolean(OlmInboundGroupSessionEntityFields.BACKED_UP)
|
|
||||||
val serializedOlmInboundGroupSession = it.getString(OlmInboundGroupSessionEntityFields.SERIALIZED_OLM_INBOUND_GROUP_SESSION)
|
|
||||||
val inboundSession = deserializeFromRealm<OlmInboundGroupSession>(serializedOlmInboundGroupSession) ?: return@transform Unit.also {
|
|
||||||
Timber.w("Rust db migration: Failed to migrated group session, no meta data")
|
|
||||||
}
|
|
||||||
val sessionData = it.getString(OlmInboundGroupSessionEntityFields.INBOUND_GROUP_SESSION_DATA_JSON).let { json ->
|
|
||||||
sessionDataAdapter.fromJson(json)
|
|
||||||
} ?: return@transform Unit.also {
|
|
||||||
Timber.w("Rust db migration: Failed to migrated group session, no meta data")
|
|
||||||
}
|
|
||||||
val pickle = inboundSession.pickle(pickleKey, StringBuffer()).asString()
|
|
||||||
val pickledSession = PickledInboundGroupSession(
|
|
||||||
pickle = pickle,
|
|
||||||
senderKey = senderKey,
|
|
||||||
signingKey = sessionData.keysClaimed.orEmpty(),
|
|
||||||
roomId = roomId,
|
|
||||||
forwardingChains = sessionData.forwardingCurve25519KeyChain.orEmpty(),
|
|
||||||
imported = sessionData.trusted.orFalse().not(),
|
|
||||||
backedUp = backedUp
|
|
||||||
)
|
|
||||||
// should we check the tracking status?
|
|
||||||
pickledSessions.add(pickledSession)
|
|
||||||
if (pickledSessions.size > chunkSize) {
|
|
||||||
onChunk(pickledSessions.toImmutableList())
|
|
||||||
pickledSessions.clear()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (pickledSessions.isNotEmpty()) {
|
|
||||||
onChunk(pickledSessions)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun OlmInboundGroupSessionEntity.toPickledInboundGroupSession(pickleKey: ByteArray): PickledInboundGroupSession? {
|
|
||||||
val senderKey = this.senderKey ?: return null
|
|
||||||
val backedUp = this.backedUp
|
|
||||||
val olmInboundGroupSession = this.getOlmGroupSession() ?: return null.also {
|
|
||||||
Timber.w("Rust db migration: Failed to migrated group session $sessionId")
|
|
||||||
}
|
|
||||||
val data = this.getData() ?: return null.also {
|
|
||||||
Timber.w("Rust db migration: Failed to migrated group session $sessionId, no meta data")
|
|
||||||
}
|
|
||||||
val roomId = data.roomId ?: return null.also {
|
|
||||||
Timber.w("Rust db migration: Failed to migrated group session $sessionId, no roomId")
|
|
||||||
}
|
|
||||||
val pickledInboundGroupSession = olmInboundGroupSession.pickle(pickleKey, StringBuffer()).asString()
|
|
||||||
return PickledInboundGroupSession(
|
|
||||||
pickle = pickledInboundGroupSession,
|
|
||||||
senderKey = senderKey,
|
|
||||||
signingKey = data.keysClaimed.orEmpty(),
|
|
||||||
roomId = roomId,
|
|
||||||
forwardingChains = data.forwardingCurve25519KeyChain.orEmpty(),
|
|
||||||
imported = data.trusted.orFalse().not(),
|
|
||||||
backedUp = backedUp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
private fun OlmSessionEntity.toPickledSession(pickleKey: ByteArray): PickledSession {
|
|
||||||
val deviceKey = this.deviceKey ?: ""
|
|
||||||
val lastReceivedMessageTs = this.lastReceivedMessageTs
|
|
||||||
val olmSessionStr = this.olmSessionData
|
|
||||||
val olmSession = deserializeFromRealm<OlmSession>(olmSessionStr)!!
|
|
||||||
val pickledOlmSession = olmSession.pickle(pickleKey, StringBuffer()).asString()
|
|
||||||
return PickledSession(
|
|
||||||
pickle = pickledOlmSession,
|
|
||||||
senderKey = deviceKey,
|
|
||||||
createdUsingFallbackKey = false,
|
|
||||||
// Rust expect in seconds
|
|
||||||
creationTime = (lastReceivedMessageTs / 1000).toULong(),
|
|
||||||
// Rust expect in seconds
|
|
||||||
lastUseTime = (lastReceivedMessageTs / 1000).toULong(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private val charset = Charset.forName("UTF-8")
|
|
||||||
private fun ByteArray.asString() = String(this, charset)
|
|
|
@ -18,9 +18,6 @@ package org.matrix.android.sdk.internal.crypto.store.db.model
|
||||||
|
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
|
||||||
import org.matrix.olm.OlmAccount
|
|
||||||
|
|
||||||
internal open class CryptoMetadataEntity(
|
internal open class CryptoMetadataEntity(
|
||||||
// The current user id.
|
// The current user id.
|
||||||
|
@ -53,15 +50,4 @@ internal open class CryptoMetadataEntity(
|
||||||
var keyBackupRecoveryKeyVersion: String? = null
|
var keyBackupRecoveryKeyVersion: String? = null
|
||||||
|
|
||||||
// var crossSigningInfoEntity: CrossSigningInfoEntity? = null
|
// var crossSigningInfoEntity: CrossSigningInfoEntity? = null
|
||||||
) : RealmObject() {
|
) : RealmObject()
|
||||||
|
|
||||||
// Deserialize data
|
|
||||||
fun getOlmAccount(): OlmAccount? {
|
|
||||||
return deserializeFromRealm(olmAccountData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Serialize data
|
|
||||||
fun putOlmAccount(olmAccount: OlmAccount?) {
|
|
||||||
olmAccountData = serializeForRealm(olmAccount)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -19,12 +19,7 @@ package org.matrix.android.sdk.internal.crypto.store.db.model
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData
|
import org.matrix.android.sdk.internal.crypto.model.InboundGroupSessionData
|
||||||
import org.matrix.android.sdk.internal.crypto.model.MXInboundMegolmSessionWrapper
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
import org.matrix.android.sdk.internal.di.MoshiProvider
|
||||||
import org.matrix.olm.OlmInboundGroupSession
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey"
|
internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey"
|
||||||
|
|
||||||
|
@ -56,14 +51,6 @@ internal open class OlmInboundGroupSessionEntity(
|
||||||
) :
|
) :
|
||||||
RealmObject() {
|
RealmObject() {
|
||||||
|
|
||||||
fun store(wrapper: MXInboundMegolmSessionWrapper) {
|
|
||||||
this.serializedOlmInboundGroupSession = serializeForRealm(wrapper.session)
|
|
||||||
this.inboundGroupSessionDataJson = adapter.toJson(wrapper.sessionData)
|
|
||||||
this.roomId = wrapper.sessionData.roomId
|
|
||||||
this.senderKey = wrapper.sessionData.senderKey
|
|
||||||
this.sessionId = wrapper.session.sessionIdentifier()
|
|
||||||
this.sharedHistory = wrapper.sessionData.sharedHistory
|
|
||||||
}
|
|
||||||
// fun getInboundGroupSession(): OlmInboundGroupSessionWrapper2? {
|
// fun getInboundGroupSession(): OlmInboundGroupSessionWrapper2? {
|
||||||
// return try {
|
// return try {
|
||||||
// deserializeFromRealm<OlmInboundGroupSessionWrapper2?>(olmInboundGroupSessionData)
|
// deserializeFromRealm<OlmInboundGroupSessionWrapper2?>(olmInboundGroupSessionData)
|
||||||
|
@ -77,35 +64,6 @@ internal open class OlmInboundGroupSessionEntity(
|
||||||
// olmInboundGroupSessionData = serializeForRealm(olmInboundGroupSessionWrapper)
|
// olmInboundGroupSessionData = serializeForRealm(olmInboundGroupSessionWrapper)
|
||||||
// }
|
// }
|
||||||
|
|
||||||
fun getOlmGroupSession(): OlmInboundGroupSession? {
|
|
||||||
return try {
|
|
||||||
deserializeFromRealm(serializedOlmInboundGroupSession)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure, "## Deserialization failure")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getData(): InboundGroupSessionData? {
|
|
||||||
return try {
|
|
||||||
inboundGroupSessionDataJson?.let {
|
|
||||||
adapter.fromJson(it)
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure, "## Deserialization failure")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toModel(): MXInboundMegolmSessionWrapper? {
|
|
||||||
val data = getData() ?: return null
|
|
||||||
val session = getOlmGroupSession() ?: return null
|
|
||||||
return MXInboundMegolmSessionWrapper(
|
|
||||||
session = session,
|
|
||||||
sessionData = data
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val adapter = MoshiProvider.providesMoshi()
|
private val adapter = MoshiProvider.providesMoshi()
|
||||||
.adapter(InboundGroupSessionData::class.java)
|
.adapter(InboundGroupSessionData::class.java)
|
||||||
|
|
|
@ -18,9 +18,6 @@ package org.matrix.android.sdk.internal.crypto.store.db.model
|
||||||
|
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
|
||||||
import org.matrix.olm.OlmSession
|
|
||||||
|
|
||||||
internal fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, deviceKey: String) = "$sessionId|$deviceKey"
|
internal fun OlmSessionEntity.Companion.createPrimaryKey(sessionId: String, deviceKey: String) = "$sessionId|$deviceKey"
|
||||||
|
|
||||||
|
@ -34,13 +31,5 @@ internal open class OlmSessionEntity(
|
||||||
) :
|
) :
|
||||||
RealmObject() {
|
RealmObject() {
|
||||||
|
|
||||||
fun getOlmSession(): OlmSession? {
|
|
||||||
return deserializeFromRealm(olmSessionData)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun putOlmSession(olmSession: OlmSession?) {
|
|
||||||
olmSessionData = serializeForRealm(olmSession)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,6 @@
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.model
|
package org.matrix.android.sdk.internal.crypto.store.db.model
|
||||||
|
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.deserializeFromRealm
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.serializeForRealm
|
|
||||||
import org.matrix.olm.OlmOutboundGroupSession
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal open class OutboundGroupSessionInfoEntity(
|
internal open class OutboundGroupSessionInfoEntity(
|
||||||
var serializedOutboundSessionData: String? = null,
|
var serializedOutboundSessionData: String? = null,
|
||||||
|
@ -28,18 +24,5 @@ internal open class OutboundGroupSessionInfoEntity(
|
||||||
var shouldShareHistory: Boolean = false
|
var shouldShareHistory: Boolean = false
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
fun getOutboundGroupSession(): OlmOutboundGroupSession? {
|
|
||||||
return try {
|
|
||||||
deserializeFromRealm(serializedOutboundSessionData)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure, "## getOutboundGroupSession() Deserialization failure")
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun putOutboundGroupSession(olmOutboundGroupSession: OlmOutboundGroupSession?) {
|
|
||||||
serializedOutboundSessionData = serializeForRealm(olmOutboundGroupSession)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,43 +16,10 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.tools
|
package org.matrix.android.sdk.internal.crypto.tools
|
||||||
|
|
||||||
import org.matrix.olm.OlmPkDecryption
|
// TODO BMA
|
||||||
import org.matrix.olm.OlmPkEncryption
|
data object OlmPkEncryption
|
||||||
import org.matrix.olm.OlmPkSigning
|
|
||||||
import org.matrix.olm.OlmUtility
|
|
||||||
|
|
||||||
internal fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
|
internal fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
|
||||||
val olmPkEncryption = OlmPkEncryption()
|
val olmPkEncryption = OlmPkEncryption
|
||||||
try {
|
return block(olmPkEncryption)
|
||||||
return block(olmPkEncryption)
|
|
||||||
} finally {
|
|
||||||
olmPkEncryption.releaseEncryption()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun <T> withOlmDecryption(block: (OlmPkDecryption) -> T): T {
|
|
||||||
val olmPkDecryption = OlmPkDecryption()
|
|
||||||
try {
|
|
||||||
return block(olmPkDecryption)
|
|
||||||
} finally {
|
|
||||||
olmPkDecryption.releaseDecryption()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun <T> withOlmSigning(block: (OlmPkSigning) -> T): T {
|
|
||||||
val olmPkSigning = OlmPkSigning()
|
|
||||||
try {
|
|
||||||
return block(olmPkSigning)
|
|
||||||
} finally {
|
|
||||||
olmPkSigning.releaseSigning()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun <T> withOlmUtility(block: (OlmUtility) -> T): T {
|
|
||||||
val olmUtility = OlmUtility()
|
|
||||||
try {
|
|
||||||
return block(olmUtility)
|
|
||||||
} finally {
|
|
||||||
olmUtility.releaseUtility()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,6 @@ import org.matrix.android.sdk.internal.task.TaskExecutor
|
||||||
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
import org.matrix.android.sdk.internal.util.BackgroundDetectionObserver
|
||||||
import org.matrix.android.sdk.internal.util.system.SystemModule
|
import org.matrix.android.sdk.internal.util.system.SystemModule
|
||||||
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
||||||
import org.matrix.olm.OlmManager
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@Component(
|
@Component(
|
||||||
|
@ -89,8 +88,6 @@ internal interface MatrixComponent {
|
||||||
@CacheDirectory
|
@CacheDirectory
|
||||||
fun cacheDir(): File
|
fun cacheDir(): File
|
||||||
|
|
||||||
fun olmManager(): OlmManager
|
|
||||||
|
|
||||||
fun taskExecutor(): TaskExecutor
|
fun taskExecutor(): TaskExecutor
|
||||||
|
|
||||||
fun sessionParamsStore(): SessionParamsStore
|
fun sessionParamsStore(): SessionParamsStore
|
||||||
|
|
|
@ -25,7 +25,6 @@ import kotlinx.coroutines.android.asCoroutineDispatcher
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.internal.util.createBackgroundHandler
|
import org.matrix.android.sdk.internal.util.createBackgroundHandler
|
||||||
import org.matrix.olm.OlmManager
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
@ -57,11 +56,4 @@ internal object MatrixModule {
|
||||||
fun providesCacheDir(context: Context): File {
|
fun providesCacheDir(context: Context): File {
|
||||||
return context.cacheDir
|
return context.cacheDir
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
@Provides
|
|
||||||
@MatrixScope
|
|
||||||
fun providesOlmManager(): OlmManager {
|
|
||||||
return OlmManager()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2022 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session
|
|
||||||
|
|
||||||
import io.realm.DynamicRealm
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmConfiguration
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.rust.ExtractMigrationDataUseCase
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.rust.RealmToMigrate
|
|
||||||
import org.matrix.rustcomponents.sdk.crypto.ProgressListener
|
|
||||||
import timber.log.Timber
|
|
||||||
import java.io.File
|
|
||||||
|
|
||||||
class MigrateEAtoEROperation(private val migrateGroupSessions: Boolean = false) {
|
|
||||||
|
|
||||||
fun execute(cryptoRealm: RealmConfiguration, rustFilesDir: File, passphrase: String?): File {
|
|
||||||
// Temporary code for migration
|
|
||||||
if (!rustFilesDir.exists()) {
|
|
||||||
rustFilesDir.mkdir()
|
|
||||||
// perform a migration?
|
|
||||||
val extractMigrationData = ExtractMigrationDataUseCase(migrateGroupSessions)
|
|
||||||
val hasExitingData = extractMigrationData.hasExistingData(cryptoRealm)
|
|
||||||
if (!hasExitingData) return rustFilesDir
|
|
||||||
|
|
||||||
try {
|
|
||||||
val progressListener = object : ProgressListener {
|
|
||||||
override fun onProgress(progress: Int, total: Int) {
|
|
||||||
Timber.v("OnProgress: $progress/$total")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Realm.getInstance(cryptoRealm).use { realm ->
|
|
||||||
extractMigrationData.extractData(RealmToMigrate.ClassicRealm(realm)) {
|
|
||||||
org.matrix.rustcomponents.sdk.crypto.migrate(it, rustFilesDir.path, passphrase, progressListener)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure, "Failure while calling rust migration method")
|
|
||||||
throw failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return rustFilesDir
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dynamicExecute(dynamicRealm: DynamicRealm, rustFilesDir: File, passphrase: String?) {
|
|
||||||
if (!rustFilesDir.exists()) {
|
|
||||||
rustFilesDir.mkdir()
|
|
||||||
}
|
|
||||||
val extractMigrationData = ExtractMigrationDataUseCase(migrateGroupSessions)
|
|
||||||
|
|
||||||
try {
|
|
||||||
val progressListener = object : ProgressListener {
|
|
||||||
override fun onProgress(progress: Int, total: Int) {
|
|
||||||
Timber.v("OnProgress: $progress/$total")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
extractMigrationData.extractData(RealmToMigrate.DynamicRealm(dynamicRealm)) {
|
|
||||||
org.matrix.rustcomponents.sdk.crypto.migrate(it, rustFilesDir.path, passphrase, progressListener)
|
|
||||||
}
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure, "Failure while calling rust migration method")
|
|
||||||
throw failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -44,9 +44,11 @@ internal object ScanEncryptorUtils {
|
||||||
)
|
)
|
||||||
return if (publicServerKey != null) {
|
return if (publicServerKey != null) {
|
||||||
// We should encrypt
|
// We should encrypt
|
||||||
withOlmEncryption { olm ->
|
withOlmEncryption { //olm ->
|
||||||
|
// TODO BMA
|
||||||
|
error("Not supported anymore")
|
||||||
|
/*
|
||||||
olm.setRecipientKey(publicServerKey)
|
olm.setRecipientKey(publicServerKey)
|
||||||
|
|
||||||
val olmResult = olm.encrypt(DownloadBody(encryptedInfo).toCanonicalJson())
|
val olmResult = olm.encrypt(DownloadBody(encryptedInfo).toCanonicalJson())
|
||||||
DownloadBody(
|
DownloadBody(
|
||||||
encryptedBody = EncryptedBody(
|
encryptedBody = EncryptedBody(
|
||||||
|
@ -55,6 +57,7 @@ internal object ScanEncryptorUtils {
|
||||||
mac = olmResult.mMac
|
mac = olmResult.mMac
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DownloadBody(encryptedInfo)
|
DownloadBody(encryptedInfo)
|
||||||
|
|
|
@ -22,7 +22,6 @@ import org.matrix.android.sdk.api.session.identity.FoundThreePid
|
||||||
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
|
import org.matrix.android.sdk.api.session.identity.IdentityServiceError
|
||||||
import org.matrix.android.sdk.api.session.identity.ThreePid
|
import org.matrix.android.sdk.api.session.identity.ThreePid
|
||||||
import org.matrix.android.sdk.api.session.identity.toMedium
|
import org.matrix.android.sdk.api.session.identity.toMedium
|
||||||
import org.matrix.android.sdk.internal.crypto.tools.withOlmUtility
|
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.network.executeRequest
|
import org.matrix.android.sdk.internal.network.executeRequest
|
||||||
import org.matrix.android.sdk.internal.session.identity.data.IdentityStore
|
import org.matrix.android.sdk.internal.session.identity.data.IdentityStore
|
||||||
|
@ -31,6 +30,7 @@ import org.matrix.android.sdk.internal.session.identity.model.IdentityLookUpPara
|
||||||
import org.matrix.android.sdk.internal.session.identity.model.IdentityLookUpResponse
|
import org.matrix.android.sdk.internal.session.identity.model.IdentityLookUpResponse
|
||||||
import org.matrix.android.sdk.internal.task.Task
|
import org.matrix.android.sdk.internal.task.Task
|
||||||
import org.matrix.android.sdk.internal.util.base64ToBase64Url
|
import org.matrix.android.sdk.internal.util.base64ToBase64Url
|
||||||
|
import java.security.MessageDigest
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@ -118,15 +118,10 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getHashedAddresses(threePids: List<ThreePid>, pepper: String): List<String> {
|
private fun getHashedAddresses(threePids: List<ThreePid>, pepper: String): List<String> {
|
||||||
return withOlmUtility { olmUtility ->
|
return threePids.map { threePid ->
|
||||||
threePids.map { threePid ->
|
base64ToBase64Url(
|
||||||
base64ToBase64Url(
|
(threePid.value.lowercase(Locale.ROOT) + " " + threePid.toMedium() + " " + pepper).toSha256()
|
||||||
olmUtility.sha256(
|
)
|
||||||
threePid.value.lowercase(Locale.ROOT) +
|
|
||||||
" " + threePid.toMedium() + " " + pepper
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,4 +139,11 @@ internal class DefaultIdentityBulkLookupTask @Inject constructor(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val sha256 by lazy { MessageDigest.getInstance("SHA-256") }
|
||||||
|
|
||||||
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
|
private fun String.toSha256(): String {
|
||||||
|
return sha256.digest(toByteArray()).toHexString()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,83 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.androidsdk.crypto.data;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>IMPORTANT:</b> This class is imported from Riot-Android to be able to perform a migration. Do not use it for any other purpose
|
|
||||||
*/
|
|
||||||
public class MXDeviceInfo implements Serializable {
|
|
||||||
private static final long serialVersionUID = 20129670646382964L;
|
|
||||||
|
|
||||||
// This device is a new device and the user was not warned it has been added.
|
|
||||||
public static final int DEVICE_VERIFICATION_UNKNOWN = -1;
|
|
||||||
|
|
||||||
// The user has not yet verified this device.
|
|
||||||
public static final int DEVICE_VERIFICATION_UNVERIFIED = 0;
|
|
||||||
|
|
||||||
// The user has verified this device.
|
|
||||||
public static final int DEVICE_VERIFICATION_VERIFIED = 1;
|
|
||||||
|
|
||||||
// The user has blocked this device.
|
|
||||||
public static final int DEVICE_VERIFICATION_BLOCKED = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The id of this device.
|
|
||||||
*/
|
|
||||||
public String deviceId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* the user id
|
|
||||||
*/
|
|
||||||
public String userId;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The list of algorithms supported by this device.
|
|
||||||
*/
|
|
||||||
public List<String> algorithms;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A map from <key type>:<id> to <base64-encoded key>>.
|
|
||||||
*/
|
|
||||||
public Map<String, String> keys;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The signature of this MXDeviceInfo.
|
|
||||||
* A map from <key type>:<device_id> to <base64-encoded key>>.
|
|
||||||
*/
|
|
||||||
public Map<String, Map<String, String>> signatures;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Additional data from the homeserver.
|
|
||||||
*/
|
|
||||||
public Map<String, Object> unsigned;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verification state of this device.
|
|
||||||
*/
|
|
||||||
public int mVerified;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
public MXDeviceInfo() {
|
|
||||||
mVerified = DEVICE_VERIFICATION_UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,50 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.matrix.androidsdk.crypto.data;
|
|
||||||
|
|
||||||
import org.matrix.olm.OlmInboundGroupSession;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <b>IMPORTANT:</b> This class is imported from Riot-Android to be able to perform a migration. Do not use it for any other purpose
|
|
||||||
*
|
|
||||||
* This class adds more context to a OLMInboundGroupSession object.
|
|
||||||
* This allows additional checks. The class implements NSCoding so that the context can be stored.
|
|
||||||
*/
|
|
||||||
public class MXOlmInboundGroupSession2 implements Serializable {
|
|
||||||
// define a serialVersionUID to avoid having to redefine the class after updates
|
|
||||||
private static final long serialVersionUID = 201702011617L;
|
|
||||||
|
|
||||||
// The associated olm inbound group session.
|
|
||||||
public OlmInboundGroupSession mSession;
|
|
||||||
|
|
||||||
// The room in which this session is used.
|
|
||||||
public String mRoomId;
|
|
||||||
|
|
||||||
// The base64-encoded curve25519 key of the sender.
|
|
||||||
public String mSenderKey;
|
|
||||||
|
|
||||||
// Other keys the sender claims.
|
|
||||||
public Map<String, String> mKeysClaimed;
|
|
||||||
|
|
||||||
// Devices which forwarded this session to us (normally empty).
|
|
||||||
public List<String> mForwardingCurve25519KeyChain = new ArrayList<>();
|
|
||||||
}
|
|
Loading…
Reference in New Issue