mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2024-11-21 17:05:39 +03:00
Merge pull request #8901 from element-hq/feature/bma/removeLibolm
Remove dependency on libolm
This commit is contained in:
commit
ac01523066
94 changed files with 159 additions and 4645 deletions
|
@ -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
|
||||||
|
@ -224,7 +221,7 @@ dependencies {
|
||||||
|
|
||||||
implementation libs.google.phonenumber
|
implementation libs.google.phonenumber
|
||||||
|
|
||||||
implementation("org.matrix.rustcomponents:crypto-android:0.4.1")
|
implementation("org.matrix.rustcomponents:crypto-android:0.4.3")
|
||||||
// api project(":library:rustCrypto")
|
// api project(":library:rustCrypto")
|
||||||
|
|
||||||
testImplementation libs.tests.junit
|
testImplementation libs.tests.junit
|
||||||
|
@ -236,6 +233,7 @@ dependencies {
|
||||||
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||||
// Transitively required for mocking realm as monarchy doesn't expose Rx
|
// Transitively required for mocking realm as monarchy doesn't expose Rx
|
||||||
testImplementation libs.rx.rxKotlin
|
testImplementation libs.rx.rxKotlin
|
||||||
|
testImplementation libs.tests.robolectric
|
||||||
|
|
||||||
kaptAndroidTest libs.dagger.daggerCompiler
|
kaptAndroidTest libs.dagger.daggerCompiler
|
||||||
androidTestImplementation libs.androidx.testCore
|
androidTestImplementation libs.androidx.testCore
|
||||||
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.session.contentscanner
|
package org.matrix.android.sdk.internal.session.contentscanner
|
||||||
|
|
||||||
import okio.ByteString.Companion.decodeBase64
|
|
||||||
import org.amshove.kluent.shouldBe
|
import org.amshove.kluent.shouldBe
|
||||||
import org.amshove.kluent.shouldBeEqualTo
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
import org.amshove.kluent.shouldNotBe
|
import org.amshove.kluent.shouldNotBe
|
||||||
|
@ -24,11 +23,7 @@ import org.junit.Test
|
||||||
import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt
|
import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
|
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileKey
|
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileKey
|
||||||
import org.matrix.android.sdk.internal.crypto.tools.withOlmDecryption
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
|
||||||
import org.matrix.android.sdk.internal.session.contentscanner.model.DownloadBody
|
import org.matrix.android.sdk.internal.session.contentscanner.model.DownloadBody
|
||||||
import org.matrix.android.sdk.internal.session.contentscanner.model.EncryptedBody
|
|
||||||
import org.matrix.olm.OlmPkMessage
|
|
||||||
|
|
||||||
class ScanEncryptorUtilsTest {
|
class ScanEncryptorUtilsTest {
|
||||||
private val anMxcUrl = "mxc://matrix.org/123456"
|
private val anMxcUrl = "mxc://matrix.org/123456"
|
||||||
|
@ -67,7 +62,6 @@ class ScanEncryptorUtilsTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun whenServerKeyIsProvidedTheContentIsEncrypted() {
|
fun whenServerKeyIsProvidedTheContentIsEncrypted() {
|
||||||
System.loadLibrary("olm")
|
|
||||||
val result = ScanEncryptorUtils.getDownloadBodyAndEncryptIfNeeded(
|
val result = ScanEncryptorUtils.getDownloadBodyAndEncryptIfNeeded(
|
||||||
publicServerKey = aPublicKey,
|
publicServerKey = aPublicKey,
|
||||||
mxcUrl = anMxcUrl,
|
mxcUrl = anMxcUrl,
|
||||||
|
@ -78,6 +72,8 @@ class ScanEncryptorUtilsTest {
|
||||||
result.encryptedBody shouldNotBe null
|
result.encryptedBody shouldNotBe null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note: PkDecryption is not exposed in the FFI layer, so we cannot use this test.
|
||||||
|
/*
|
||||||
@Test
|
@Test
|
||||||
fun checkThatTheCodeIsAbleToDecryptContent() {
|
fun checkThatTheCodeIsAbleToDecryptContent() {
|
||||||
System.loadLibrary("olm")
|
System.loadLibrary("olm")
|
||||||
|
@ -121,4 +117,5 @@ class ScanEncryptorUtilsTest {
|
||||||
.fromJson(result)
|
.fromJson(result)
|
||||||
parseResult shouldBeEqualTo clearInfo
|
parseResult shouldBeEqualTo clearInfo
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -31,11 +31,6 @@ const val MXCRYPTO_ALGORITHM_MEGOLM = "m.megolm.v1.aes-sha2"
|
||||||
*/
|
*/
|
||||||
const val MXCRYPTO_ALGORITHM_MEGOLM_BACKUP = "m.megolm_backup.v1.curve25519-aes-sha2"
|
const val MXCRYPTO_ALGORITHM_MEGOLM_BACKUP = "m.megolm_backup.v1.curve25519-aes-sha2"
|
||||||
|
|
||||||
/**
|
|
||||||
* Secured Shared Storage algorithm constant.
|
|
||||||
*/
|
|
||||||
const val SSSS_ALGORITHM_CURVE25519_AES_SHA2 = "m.secret_storage.v1.curve25519-aes-sha2"
|
|
||||||
|
|
||||||
/* Secrets are encrypted using AES-CTR-256 and MACed using HMAC-SHA-256. **/
|
/* Secrets are encrypted using AES-CTR-256 and MACed using HMAC-SHA-256. **/
|
||||||
const val SSSS_ALGORITHM_AES_HMAC_SHA2 = "m.secret_storage.v1.aes-hmac-sha2"
|
const val SSSS_ALGORITHM_AES_HMAC_SHA2 = "m.secret_storage.v1.aes-hmac-sha2"
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
*
|
*
|
||||||
|
|
|
@ -121,7 +121,8 @@ internal class PrepareToEncryptUseCase @Inject constructor(
|
||||||
HistoryVisibility.INVITED
|
HistoryVisibility.INVITED
|
||||||
} else {
|
} else {
|
||||||
HistoryVisibility.JOINED
|
HistoryVisibility.JOINED
|
||||||
}
|
},
|
||||||
|
errorOnVerifiedUserProblem = false,
|
||||||
)
|
)
|
||||||
measureTimeMillis {
|
measureTimeMillis {
|
||||||
keyShareLock.withLock {
|
keyShareLock.withLock {
|
||||||
|
|
|
@ -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
|
|
||||||
)
|
|
|
@ -20,7 +20,6 @@ import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||||
import org.matrix.android.sdk.api.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
import org.matrix.android.sdk.api.crypto.SSSS_ALGORITHM_AES_HMAC_SHA2
|
||||||
import org.matrix.android.sdk.api.crypto.SSSS_ALGORITHM_CURVE25519_AES_SHA2
|
|
||||||
import org.matrix.android.sdk.api.extensions.orFalse
|
import org.matrix.android.sdk.api.extensions.orFalse
|
||||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||||
import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
|
import org.matrix.android.sdk.api.session.accountdata.SessionAccountDataService
|
||||||
|
@ -44,9 +43,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
|
||||||
|
@ -321,22 +318,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
?: throw SharedSecretStorageError.ParsingError
|
?: throw SharedSecretStorageError.ParsingError
|
||||||
|
|
||||||
val algorithm = key.keyInfo.content
|
val algorithm = key.keyInfo.content
|
||||||
if (SSSS_ALGORITHM_CURVE25519_AES_SHA2 == algorithm.algorithm) {
|
if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) {
|
||||||
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
|
|
||||||
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
|
|
||||||
// decrypt from recovery key
|
|
||||||
withOlmDecryption { olmPkDecryption ->
|
|
||||||
olmPkDecryption.setPrivateKey(keySpec.privateKey)
|
|
||||||
olmPkDecryption.decrypt(OlmPkMessage()
|
|
||||||
.apply {
|
|
||||||
mCipherText = secretContent.ciphertext
|
|
||||||
mEphemeralKey = secretContent.ephemeral
|
|
||||||
mMac = secretContent.mac
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} 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
|
||||||
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
|
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
|
||||||
decryptAesHmacSha2(keySpec, name, secretContent)
|
decryptAesHmacSha2(keySpec, name, secretContent)
|
||||||
|
@ -366,8 +348,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
||||||
val keyInfo = (keyInfoResult as? KeyInfoResult.Success)?.keyInfo
|
val keyInfo = (keyInfoResult as? KeyInfoResult.Success)?.keyInfo
|
||||||
?: return IntegrityResult.Error(SharedSecretStorageError.UnknownKey(keyId ?: ""))
|
?: return IntegrityResult.Error(SharedSecretStorageError.UnknownKey(keyId ?: ""))
|
||||||
|
|
||||||
if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2 &&
|
if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2) {
|
||||||
keyInfo.content.algorithm != SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
|
|
||||||
// Unsupported algorithm
|
// Unsupported algorithm
|
||||||
return IntegrityResult.Error(
|
return IntegrityResult.Error(
|
||||||
SharedSecretStorageError.UnsupportedAlgorithm(keyInfo.content.algorithm ?: "")
|
SharedSecretStorageError.UnsupportedAlgorithm(keyInfo.content.algorithm ?: "")
|
||||||
|
|
|
@ -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,32 +35,23 @@ 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
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.doWithRealm
|
import org.matrix.android.sdk.internal.crypto.store.db.doWithRealm
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CryptoRoomInfoMapper
|
import org.matrix.android.sdk.internal.crypto.store.db.mapper.CryptoRoomInfoMapper
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.mapper.MyDeviceLastSeenInfoEntityMapper
|
import org.matrix.android.sdk.internal.crypto.store.db.mapper.MyDeviceLastSeenInfoEntityMapper
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.AuditTrailEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.AuditTrailEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity
|
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.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
|
||||||
import org.matrix.android.sdk.internal.di.DeviceId
|
import org.matrix.android.sdk.internal.di.DeviceId
|
||||||
import org.matrix.android.sdk.internal.di.UserId
|
import org.matrix.android.sdk.internal.di.UserId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.android.sdk.internal.util.time.Clock
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.concurrent.TimeUnit
|
import java.util.concurrent.TimeUnit
|
||||||
|
@ -75,7 +66,6 @@ private val loggerTag = LoggerTag("RealmCryptoStore", LoggerTag.CRYPTO)
|
||||||
@SessionScope
|
@SessionScope
|
||||||
internal class RustCryptoStore @Inject constructor(
|
internal class RustCryptoStore @Inject constructor(
|
||||||
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
||||||
private val clock: Clock,
|
|
||||||
@UserId private val userId: String,
|
@UserId private val userId: String,
|
||||||
@DeviceId private val deviceId: String,
|
@DeviceId private val deviceId: String,
|
||||||
private val myDeviceLastSeenInfoEntityMapper: MyDeviceLastSeenInfoEntityMapper,
|
private val myDeviceLastSeenInfoEntityMapper: MyDeviceLastSeenInfoEntityMapper,
|
||||||
|
@ -134,20 +124,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
|
||||||
// ================================================
|
// ================================================
|
||||||
|
@ -163,30 +139,7 @@ internal class RustCryptoStore @Inject constructor(
|
||||||
// nop
|
// nop
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun tidyUpDataBase() {
|
override fun tidyUpDataBase() = Unit
|
||||||
// These entities are not used in rust actually, but as they are not yet cleaned up, this will do it with time
|
|
||||||
val prevWeekTs = clock.epochMillis() - 7 * 24 * 60 * 60 * 1_000
|
|
||||||
doRealmTransaction("tidyUpDataBase", realmConfiguration) { realm ->
|
|
||||||
|
|
||||||
// Clean the old ones?
|
|
||||||
realm.where<OutgoingKeyRequestEntity>()
|
|
||||||
.lessThan(OutgoingKeyRequestEntityFields.CREATION_TIME_STAMP, prevWeekTs)
|
|
||||||
.findAll()
|
|
||||||
.also { Timber.i("## Crypto Clean up ${it.size} OutgoingKeyRequestEntity") }
|
|
||||||
.deleteAllFromRealm()
|
|
||||||
|
|
||||||
// Only keep one month history
|
|
||||||
|
|
||||||
val prevMonthTs = clock.epochMillis() - 4 * 7 * 24 * 60 * 60 * 1_000L
|
|
||||||
realm.where<AuditTrailEntity>()
|
|
||||||
.lessThan(AuditTrailEntityFields.AGE_LOCAL_TS, prevMonthTs)
|
|
||||||
.findAll()
|
|
||||||
.also { Timber.i("## Crypto Clean up ${it.size} AuditTrailEntity") }
|
|
||||||
.deleteAllFromRealm()
|
|
||||||
|
|
||||||
// Can we do something for WithHeldSessionEntity?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
override fun close() {
|
||||||
val tasks = monarchyWriteAsyncExecutor.shutdownNow()
|
val tasks = monarchyWriteAsyncExecutor.shutdownNow()
|
||||||
|
|
|
@ -1,31 +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
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.UserIdentity
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
|
||||||
|
|
||||||
internal data class UserDataToStore(
|
|
||||||
/**
|
|
||||||
* Map of userId -> (Map of deviceId -> [CryptoDeviceInfo]).
|
|
||||||
*/
|
|
||||||
val userDevices: MutableMap<String, Map<String, CryptoDeviceInfo>> = mutableMapOf(),
|
|
||||||
/**
|
|
||||||
* Map of userId -> [UserIdentity].
|
|
||||||
*/
|
|
||||||
val userIdentities: MutableMap<String, UserIdentity> = mutableMapOf(),
|
|
||||||
)
|
|
|
@ -16,15 +16,9 @@
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db
|
package org.matrix.android.sdk.internal.crypto.store.db
|
||||||
|
|
||||||
import android.util.Base64
|
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import io.realm.RealmObject
|
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import java.io.ByteArrayOutputStream
|
|
||||||
import java.io.ObjectOutputStream
|
|
||||||
import java.util.zip.GZIPInputStream
|
|
||||||
import java.util.zip.GZIPOutputStream
|
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,24 +30,6 @@ internal fun <T> doWithRealm(realmConfiguration: RealmConfiguration, action: (Re
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get realm, do the query, copy from realm, close realm, and return the copied result.
|
|
||||||
*/
|
|
||||||
internal fun <T : RealmObject> doRealmQueryAndCopy(realmConfiguration: RealmConfiguration, action: (Realm) -> T?): T? {
|
|
||||||
return Realm.getInstance(realmConfiguration).use { realm ->
|
|
||||||
action.invoke(realm)?.let { realm.copyFromRealm(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get realm, do the list query, copy from realm, close realm, and return the copied result.
|
|
||||||
*/
|
|
||||||
internal fun <T : RealmObject> doRealmQueryAndCopyList(realmConfiguration: RealmConfiguration, action: (Realm) -> Iterable<T>): Iterable<T> {
|
|
||||||
return Realm.getInstance(realmConfiguration).use { realm ->
|
|
||||||
action.invoke(realm).let { realm.copyFromRealm(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get realm instance, invoke the action in a transaction and close realm.
|
* Get realm instance, invoke the action in a transaction and close realm.
|
||||||
*/
|
*/
|
||||||
|
@ -70,38 +46,3 @@ internal fun doRealmTransactionAsync(realmConfiguration: RealmConfiguration, act
|
||||||
realm.executeTransactionAsync { action.invoke(it) }
|
realm.executeTransactionAsync { action.invoke(it) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Serialize any Serializable object, zip it and convert to Base64 String.
|
|
||||||
*/
|
|
||||||
internal fun serializeForRealm(o: Any?): String? {
|
|
||||||
if (o == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val baos = ByteArrayOutputStream()
|
|
||||||
val gzis = GZIPOutputStream(baos)
|
|
||||||
val out = ObjectOutputStream(gzis)
|
|
||||||
out.use {
|
|
||||||
it.writeObject(o)
|
|
||||||
}
|
|
||||||
return Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Do the opposite of serializeForRealm.
|
|
||||||
*/
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
|
||||||
internal fun <T> deserializeFromRealm(string: String?): T? {
|
|
||||||
if (string == null) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
val decodedB64 = Base64.decode(string.toByteArray(), Base64.DEFAULT)
|
|
||||||
|
|
||||||
val bais = decodedB64.inputStream()
|
|
||||||
val gzis = GZIPInputStream(bais)
|
|
||||||
val ois = SafeObjectInputStream(gzis)
|
|
||||||
return ois.use {
|
|
||||||
it.readObject() as T
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,31 +17,8 @@
|
||||||
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.MigrateCryptoTo024
|
||||||
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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -49,13 +26,11 @@ import javax.inject.Inject
|
||||||
* 0, 1, 2: legacy Riot-Android;
|
* 0, 1, 2: legacy Riot-Android;
|
||||||
* 3: migrate to RiotX schema;
|
* 3: migrate to RiotX schema;
|
||||||
* 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).
|
||||||
|
* 24: Delete nearly all the crypto DB
|
||||||
*/
|
*/
|
||||||
internal class RealmCryptoStoreMigration @Inject constructor(
|
internal class RealmCryptoStoreMigration @Inject constructor() : MatrixRealmMigration(
|
||||||
private val clock: Clock,
|
|
||||||
private val rustMigrationInfoProvider: RustMigrationInfoProvider,
|
|
||||||
) : 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 +40,6 @@ 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()
|
if (oldVersion < 24) MigrateCryptoTo024(realm).perform()
|
||||||
if (oldVersion < 2) MigrateCryptoTo002Legacy(realm).perform()
|
|
||||||
if (oldVersion < 3) MigrateCryptoTo003RiotX(realm).perform()
|
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,23 +17,9 @@
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db
|
package org.matrix.android.sdk.internal.crypto.store.db
|
||||||
|
|
||||||
import io.realm.annotations.RealmModule
|
import io.realm.annotations.RealmModule
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.AuditTrailEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoRoomEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyInfoEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeyRequestReplyEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity
|
|
||||||
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.OlmInboundGroupSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingKeyRequestEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.TrustLevelEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Realm module for Crypto store classes.
|
* Realm module for Crypto store classes.
|
||||||
|
@ -43,21 +29,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEnti
|
||||||
classes = [
|
classes = [
|
||||||
CryptoMetadataEntity::class,
|
CryptoMetadataEntity::class,
|
||||||
CryptoRoomEntity::class,
|
CryptoRoomEntity::class,
|
||||||
DeviceInfoEntity::class,
|
|
||||||
KeysBackupDataEntity::class,
|
|
||||||
OlmInboundGroupSessionEntity::class,
|
|
||||||
OlmSessionEntity::class,
|
|
||||||
UserEntity::class,
|
|
||||||
KeyInfoEntity::class,
|
|
||||||
CrossSigningInfoEntity::class,
|
|
||||||
TrustLevelEntity::class,
|
|
||||||
AuditTrailEntity::class,
|
|
||||||
OutgoingKeyRequestEntity::class,
|
|
||||||
KeyRequestReplyEntity::class,
|
|
||||||
MyDeviceLastSeenInfoEntity::class,
|
MyDeviceLastSeenInfoEntity::class,
|
||||||
WithHeldSessionEntity::class,
|
|
||||||
SharedSessionEntity::class,
|
|
||||||
OutboundGroupSessionInfoEntity::class
|
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
internal class RealmCryptoStoreModule
|
internal class RealmCryptoStoreModule
|
||||||
|
|
|
@ -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,44 +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
|
|
||||||
|
|
||||||
import java.io.IOException
|
|
||||||
import java.io.InputStream
|
|
||||||
import java.io.ObjectInputStream
|
|
||||||
import java.io.ObjectStreamClass
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Package has been renamed from `im.vector.matrix.android` to `org.matrix.android.sdk`
|
|
||||||
* so ensure deserialization of previously stored objects still works
|
|
||||||
*
|
|
||||||
* Ref: https://stackoverflow.com/questions/3884492/how-can-i-change-package-for-a-bunch-of-java-serializable-classes
|
|
||||||
*/
|
|
||||||
internal class SafeObjectInputStream(inputStream: InputStream) : ObjectInputStream(inputStream) {
|
|
||||||
|
|
||||||
init {
|
|
||||||
enableResolveObject(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Throws(IOException::class, ClassNotFoundException::class)
|
|
||||||
override fun readClassDescriptor(): ObjectStreamClass {
|
|
||||||
val read = super.readClassDescriptor()
|
|
||||||
if (read.name.startsWith("im.vector.matrix.android.")) {
|
|
||||||
return ObjectStreamClass.lookup(Class.forName(read.name.replace("im.vector.matrix.android.", "org.matrix.android.sdk.")))
|
|
||||||
}
|
|
||||||
return read
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,51 @@
|
||||||
|
/*
|
||||||
|
* 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.util.database.RealmMigrator
|
||||||
|
|
||||||
|
internal class MigrateCryptoTo024(realm: DynamicRealm) : RealmMigrator(realm, 24) {
|
||||||
|
/**
|
||||||
|
* Delete the whole DB, except tables that are still used to store data.
|
||||||
|
* Keep:
|
||||||
|
* - CryptoMetadataEntity
|
||||||
|
* - MyDeviceLastSeenInfoEntity
|
||||||
|
* - CryptoRoomEntity (but remove unused member 'outboundSessionInfo: OutboundGroupSessionInfoEntity')
|
||||||
|
*/
|
||||||
|
override fun doMigrate(realm: DynamicRealm) {
|
||||||
|
with(realm.schema) {
|
||||||
|
get("CryptoRoomEntity")?.removeField("outboundSessionInfo")
|
||||||
|
|
||||||
|
// Warning: order is important, first remove classes that depends on others.
|
||||||
|
remove("UserEntity")
|
||||||
|
remove("DeviceInfoEntity")
|
||||||
|
remove("CrossSigningInfoEntity")
|
||||||
|
remove("KeyInfoEntity")
|
||||||
|
remove("TrustLevelEntity")
|
||||||
|
remove("KeysBackupDataEntity")
|
||||||
|
remove("OlmInboundGroupSessionEntity")
|
||||||
|
remove("OlmSessionEntity")
|
||||||
|
remove("AuditTrailEntity")
|
||||||
|
remove("OutgoingKeyRequestEntity")
|
||||||
|
remove("KeyRequestReplyEntity")
|
||||||
|
remove("WithHeldSessionEntity")
|
||||||
|
remove("SharedSessionEntity")
|
||||||
|
remove("OutboundGroupSessionInfoEntity")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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)
|
|
|
@ -1,28 +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.db.model
|
|
||||||
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.annotations.Index
|
|
||||||
|
|
||||||
internal open class AuditTrailEntity(
|
|
||||||
var ageLocalTs: Long? = null,
|
|
||||||
@Index var type: String? = null,
|
|
||||||
var contentJson: String? = null
|
|
||||||
) : RealmObject() {
|
|
||||||
companion object
|
|
||||||
}
|
|
|
@ -1,82 +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.db.model
|
|
||||||
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.AuditTrail
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.ForwardInfo
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.IncomingKeyRequestInfo
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.TrailType
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.UnknownInfo
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.WithheldInfo
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
|
||||||
|
|
||||||
internal object AuditTrailMapper {
|
|
||||||
|
|
||||||
fun map(entity: AuditTrailEntity): AuditTrail? {
|
|
||||||
val contentJson = entity.contentJson ?: return null
|
|
||||||
return when (entity.type) {
|
|
||||||
TrailType.OutgoingKeyForward.name -> {
|
|
||||||
val info = tryOrNull {
|
|
||||||
MoshiProvider.providesMoshi().adapter(ForwardInfo::class.java).fromJson(contentJson)
|
|
||||||
} ?: return null
|
|
||||||
AuditTrail(
|
|
||||||
ageLocalTs = entity.ageLocalTs ?: 0,
|
|
||||||
type = TrailType.OutgoingKeyForward,
|
|
||||||
info = info
|
|
||||||
)
|
|
||||||
}
|
|
||||||
TrailType.OutgoingKeyWithheld.name -> {
|
|
||||||
val info = tryOrNull {
|
|
||||||
MoshiProvider.providesMoshi().adapter(WithheldInfo::class.java).fromJson(contentJson)
|
|
||||||
} ?: return null
|
|
||||||
AuditTrail(
|
|
||||||
ageLocalTs = entity.ageLocalTs ?: 0,
|
|
||||||
type = TrailType.OutgoingKeyWithheld,
|
|
||||||
info = info
|
|
||||||
)
|
|
||||||
}
|
|
||||||
TrailType.IncomingKeyRequest.name -> {
|
|
||||||
val info = tryOrNull {
|
|
||||||
MoshiProvider.providesMoshi().adapter(IncomingKeyRequestInfo::class.java).fromJson(contentJson)
|
|
||||||
} ?: return null
|
|
||||||
AuditTrail(
|
|
||||||
ageLocalTs = entity.ageLocalTs ?: 0,
|
|
||||||
type = TrailType.IncomingKeyRequest,
|
|
||||||
info = info
|
|
||||||
)
|
|
||||||
}
|
|
||||||
TrailType.IncomingKeyForward.name -> {
|
|
||||||
val info = tryOrNull {
|
|
||||||
MoshiProvider.providesMoshi().adapter(ForwardInfo::class.java).fromJson(contentJson)
|
|
||||||
} ?: return null
|
|
||||||
AuditTrail(
|
|
||||||
ageLocalTs = entity.ageLocalTs ?: 0,
|
|
||||||
type = TrailType.IncomingKeyForward,
|
|
||||||
info = info
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
AuditTrail(
|
|
||||||
ageLocalTs = entity.ageLocalTs ?: 0,
|
|
||||||
type = TrailType.Unknown,
|
|
||||||
info = UnknownInfo
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,65 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmList
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.annotations.PrimaryKey
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.KeyUsage
|
|
||||||
import org.matrix.android.sdk.internal.extensions.clearWith
|
|
||||||
|
|
||||||
internal open class CrossSigningInfoEntity(
|
|
||||||
@PrimaryKey
|
|
||||||
var userId: String? = null,
|
|
||||||
var wasUserVerifiedOnce: Boolean = false,
|
|
||||||
var crossSigningKeys: RealmList<KeyInfoEntity> = RealmList()
|
|
||||||
) : RealmObject() {
|
|
||||||
|
|
||||||
companion object
|
|
||||||
|
|
||||||
fun getMasterKey() = crossSigningKeys.firstOrNull { it.usages.contains(KeyUsage.MASTER.value) }
|
|
||||||
|
|
||||||
fun setMasterKey(info: KeyInfoEntity?) {
|
|
||||||
crossSigningKeys
|
|
||||||
.filter { it.usages.contains(KeyUsage.MASTER.value) }
|
|
||||||
.forEach { crossSigningKeys.remove(it) }
|
|
||||||
info?.let { crossSigningKeys.add(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSelfSignedKey() = crossSigningKeys.firstOrNull { it.usages.contains(KeyUsage.SELF_SIGNING.value) }
|
|
||||||
|
|
||||||
fun setSelfSignedKey(info: KeyInfoEntity?) {
|
|
||||||
crossSigningKeys
|
|
||||||
.filter { it.usages.contains(KeyUsage.SELF_SIGNING.value) }
|
|
||||||
.forEach { crossSigningKeys.remove(it) }
|
|
||||||
info?.let { crossSigningKeys.add(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getUserSigningKey() = crossSigningKeys.firstOrNull { it.usages.contains(KeyUsage.USER_SIGNING.value) }
|
|
||||||
|
|
||||||
fun setUserSignedKey(info: KeyInfoEntity?) {
|
|
||||||
crossSigningKeys
|
|
||||||
.filter { it.usages.contains(KeyUsage.USER_SIGNING.value) }
|
|
||||||
.forEach { crossSigningKeys.remove(it) }
|
|
||||||
info?.let { crossSigningKeys.add(it) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun CrossSigningInfoEntity.deleteOnCascade() {
|
|
||||||
crossSigningKeys.clearWith { it.deleteOnCascade() }
|
|
||||||
deleteFromRealm()
|
|
||||||
}
|
|
|
@ -1,123 +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.model
|
|
||||||
|
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import com.squareup.moshi.Types
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.UnsignedDeviceInfo
|
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
|
||||||
import org.matrix.android.sdk.internal.di.SerializeNulls
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal object CryptoMapper {
|
|
||||||
|
|
||||||
private val moshi = Moshi.Builder().add(SerializeNulls.JSON_ADAPTER_FACTORY).build()
|
|
||||||
private val listMigrationAdapter = moshi.adapter<List<String>>(
|
|
||||||
Types.newParameterizedType(
|
|
||||||
List::class.java,
|
|
||||||
String::class.java,
|
|
||||||
Any::class.java
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private val mapMigrationAdapter = moshi.adapter<JsonDict>(
|
|
||||||
Types.newParameterizedType(
|
|
||||||
Map::class.java,
|
|
||||||
String::class.java,
|
|
||||||
Any::class.java
|
|
||||||
)
|
|
||||||
)
|
|
||||||
private val mapOfStringMigrationAdapter = moshi.adapter<Map<String, Map<String, String>>>(
|
|
||||||
Types.newParameterizedType(
|
|
||||||
Map::class.java,
|
|
||||||
String::class.java,
|
|
||||||
Any::class.java
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
internal fun mapToEntity(deviceInfo: CryptoDeviceInfo): DeviceInfoEntity {
|
|
||||||
return DeviceInfoEntity(primaryKey = DeviceInfoEntity.createPrimaryKey(deviceInfo.userId, deviceInfo.deviceId))
|
|
||||||
.also { updateDeviceInfoEntity(it, deviceInfo) }
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun updateDeviceInfoEntity(entity: DeviceInfoEntity, deviceInfo: CryptoDeviceInfo) {
|
|
||||||
entity.userId = deviceInfo.userId
|
|
||||||
entity.deviceId = deviceInfo.deviceId
|
|
||||||
entity.algorithmListJson = listMigrationAdapter.toJson(deviceInfo.algorithms)
|
|
||||||
entity.keysMapJson = mapMigrationAdapter.toJson(deviceInfo.keys)
|
|
||||||
entity.signatureMapJson = mapMigrationAdapter.toJson(deviceInfo.signatures)
|
|
||||||
entity.isBlocked = deviceInfo.isBlocked
|
|
||||||
val deviceInfoTrustLevel = deviceInfo.trustLevel
|
|
||||||
if (deviceInfoTrustLevel == null) {
|
|
||||||
entity.trustLevelEntity?.deleteFromRealm()
|
|
||||||
entity.trustLevelEntity = null
|
|
||||||
} else {
|
|
||||||
if (entity.trustLevelEntity == null) {
|
|
||||||
// Create a new TrustLevelEntity object
|
|
||||||
entity.trustLevelEntity = TrustLevelEntity()
|
|
||||||
}
|
|
||||||
// Update the existing TrustLevelEntity object
|
|
||||||
entity.trustLevelEntity?.crossSignedVerified = deviceInfoTrustLevel.crossSigningVerified
|
|
||||||
entity.trustLevelEntity?.locallyVerified = deviceInfoTrustLevel.locallyVerified
|
|
||||||
}
|
|
||||||
// We store the device name if present now
|
|
||||||
entity.unsignedMapJson = deviceInfo.unsigned?.deviceDisplayName
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun mapToModel(deviceInfoEntity: DeviceInfoEntity): CryptoDeviceInfo {
|
|
||||||
return CryptoDeviceInfo(
|
|
||||||
userId = deviceInfoEntity.userId ?: "",
|
|
||||||
deviceId = deviceInfoEntity.deviceId ?: "",
|
|
||||||
isBlocked = deviceInfoEntity.isBlocked ?: false,
|
|
||||||
trustLevel = deviceInfoEntity.trustLevelEntity?.let {
|
|
||||||
DeviceTrustLevel(it.crossSignedVerified ?: false, it.locallyVerified)
|
|
||||||
},
|
|
||||||
unsigned = deviceInfoEntity.unsignedMapJson?.let { UnsignedDeviceInfo(deviceDisplayName = it) },
|
|
||||||
signatures = deviceInfoEntity.signatureMapJson?.let {
|
|
||||||
try {
|
|
||||||
mapOfStringMigrationAdapter.fromJson(it)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
keys = deviceInfoEntity.keysMapJson?.let {
|
|
||||||
try {
|
|
||||||
moshi.adapter<Map<String, String>>(
|
|
||||||
Types.newParameterizedType(
|
|
||||||
Map::class.java,
|
|
||||||
String::class.java,
|
|
||||||
Any::class.java
|
|
||||||
)
|
|
||||||
).fromJson(it)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
algorithms = deviceInfoEntity.algorithmListJson?.let {
|
|
||||||
try {
|
|
||||||
listMigrationAdapter.fromJson(it)
|
|
||||||
} catch (failure: Throwable) {
|
|
||||||
Timber.e(failure)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
firstTimeSeenLocalTs = deviceInfoEntity.firstTimeSeenLocalTs
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,10 +26,6 @@ internal open class CryptoRoomEntity(
|
||||||
var blacklistUnverifiedDevices: Boolean = false,
|
var blacklistUnverifiedDevices: Boolean = false,
|
||||||
// Determines whether or not room history should be shared on new member invites
|
// Determines whether or not room history should be shared on new member invites
|
||||||
var shouldShareHistory: Boolean = false,
|
var shouldShareHistory: Boolean = false,
|
||||||
// Store the current outbound session for this room,
|
|
||||||
// to avoid re-create and re-share at each startup (if rotation not needed..)
|
|
||||||
// This is specific to megolm but not sure how to model it better
|
|
||||||
var outboundSessionInfo: OutboundGroupSessionInfoEntity? = null,
|
|
||||||
// a security to ensure that a room will never revert to not encrypted
|
// a security to ensure that a room will never revert to not encrypted
|
||||||
// even if a new state event with empty encryption, or state is reset somehow
|
// even if a new state event with empty encryption, or state is reset somehow
|
||||||
var wasEncryptedOnce: Boolean? = false,
|
var wasEncryptedOnce: Boolean? = false,
|
||||||
|
|
|
@ -1,54 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.RealmResults
|
|
||||||
import io.realm.annotations.LinkingObjects
|
|
||||||
import io.realm.annotations.PrimaryKey
|
|
||||||
|
|
||||||
internal fun DeviceInfoEntity.Companion.createPrimaryKey(userId: String, deviceId: String) = "$userId|$deviceId"
|
|
||||||
|
|
||||||
internal open class DeviceInfoEntity(
|
|
||||||
@PrimaryKey var primaryKey: String = "",
|
|
||||||
var deviceId: String? = null,
|
|
||||||
var identityKey: String? = null,
|
|
||||||
var userId: String? = null,
|
|
||||||
var isBlocked: Boolean? = null,
|
|
||||||
var algorithmListJson: String? = null,
|
|
||||||
var keysMapJson: String? = null,
|
|
||||||
var signatureMapJson: String? = null,
|
|
||||||
// Will contain the device name from unsigned data if present
|
|
||||||
var unsignedMapJson: String? = null,
|
|
||||||
var trustLevelEntity: TrustLevelEntity? = null,
|
|
||||||
/**
|
|
||||||
* We use that to make distinction between old devices (there before mine)
|
|
||||||
* and new ones. Used for example to detect new unverified login
|
|
||||||
*/
|
|
||||||
var firstTimeSeenLocalTs: Long? = null
|
|
||||||
) : RealmObject() {
|
|
||||||
|
|
||||||
@LinkingObjects("devices")
|
|
||||||
val users: RealmResults<UserEntity>? = null
|
|
||||||
|
|
||||||
companion object
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun DeviceInfoEntity.deleteOnCascade() {
|
|
||||||
trustLevelEntity?.deleteFromRealm()
|
|
||||||
deleteFromRealm()
|
|
||||||
}
|
|
|
@ -1,37 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmList
|
|
||||||
import io.realm.RealmObject
|
|
||||||
|
|
||||||
internal open class KeyInfoEntity(
|
|
||||||
var publicKeyBase64: String? = null,
|
|
||||||
// var isTrusted: Boolean = false,
|
|
||||||
var usages: RealmList<String> = RealmList(),
|
|
||||||
/**
|
|
||||||
* The signature of this MXDeviceInfo.
|
|
||||||
* A map from "<userId>" to a map from "<key type>:<Publickey>" to "<signature>"
|
|
||||||
*/
|
|
||||||
var signatures: String? = null,
|
|
||||||
var trustLevelEntity: TrustLevelEntity? = null
|
|
||||||
) : RealmObject()
|
|
||||||
|
|
||||||
internal fun KeyInfoEntity.deleteOnCascade() {
|
|
||||||
trustLevelEntity?.deleteFromRealm()
|
|
||||||
deleteFromRealm()
|
|
||||||
}
|
|
|
@ -1,35 +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.db.model
|
|
||||||
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
|
||||||
|
|
||||||
internal open class KeyRequestReplyEntity(
|
|
||||||
var senderId: String? = null,
|
|
||||||
var fromDevice: String? = null,
|
|
||||||
var eventJson: String? = null
|
|
||||||
) : RealmObject() {
|
|
||||||
companion object
|
|
||||||
|
|
||||||
fun getEvent(): Event? {
|
|
||||||
return eventJson?.let {
|
|
||||||
MoshiProvider.providesMoshi().adapter(Event::class.java).fromJson(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.annotations.PrimaryKey
|
|
||||||
|
|
||||||
internal open class KeysBackupDataEntity(
|
|
||||||
// Primary key to update this object. There is only one object, so it's a constant, please do not set it
|
|
||||||
@PrimaryKey
|
|
||||||
var primaryKey: Int = 0,
|
|
||||||
// The last known hash of the backed up keys on the server
|
|
||||||
var backupLastServerHash: String? = null,
|
|
||||||
// The last known number of backed up keys on the server
|
|
||||||
var backupLastServerNumberOfKeys: Int? = null
|
|
||||||
) : RealmObject()
|
|
|
@ -1,113 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.annotations.PrimaryKey
|
|
||||||
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.olm.OlmInboundGroupSession
|
|
||||||
import timber.log.Timber
|
|
||||||
|
|
||||||
internal fun OlmInboundGroupSessionEntity.Companion.createPrimaryKey(sessionId: String?, senderKey: String?) = "$sessionId|$senderKey"
|
|
||||||
|
|
||||||
internal open class OlmInboundGroupSessionEntity(
|
|
||||||
// Combined value to build a primary key
|
|
||||||
@PrimaryKey var primaryKey: String? = null,
|
|
||||||
|
|
||||||
// denormalization for faster querying (these fields are in the inboundGroupSessionDataJson)
|
|
||||||
var sessionId: String? = null,
|
|
||||||
var senderKey: String? = null,
|
|
||||||
var roomId: String? = null,
|
|
||||||
|
|
||||||
// Deprecated, used for migration / olmInboundGroupSessionData contains Json
|
|
||||||
// keep it in case of problem to have a chance to recover
|
|
||||||
var olmInboundGroupSessionData: String? = null,
|
|
||||||
|
|
||||||
// Stores the session data in an extensible format
|
|
||||||
// to allow to store data not yet supported for later use
|
|
||||||
var inboundGroupSessionDataJson: String? = null,
|
|
||||||
|
|
||||||
// The pickled session
|
|
||||||
var serializedOlmInboundGroupSession: String? = null,
|
|
||||||
|
|
||||||
// Flag that indicates whether or not the current inboundSession will be shared to
|
|
||||||
// invited users to decrypt past messages
|
|
||||||
var sharedHistory: Boolean = false,
|
|
||||||
// Indicate if the key has been backed up to the homeserver
|
|
||||||
var backedUp: Boolean = false
|
|
||||||
) :
|
|
||||||
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? {
|
|
||||||
// return try {
|
|
||||||
// deserializeFromRealm<OlmInboundGroupSessionWrapper2?>(olmInboundGroupSessionData)
|
|
||||||
// } catch (failure: Throwable) {
|
|
||||||
// Timber.e(failure, "## Deserialization failure")
|
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// fun putInboundGroupSession(olmInboundGroupSessionWrapper: OlmInboundGroupSessionWrapper2?) {
|
|
||||||
// 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 {
|
|
||||||
private val adapter = MoshiProvider.providesMoshi()
|
|
||||||
.adapter(InboundGroupSessionData::class.java)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmObject
|
|
||||||
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"
|
|
||||||
|
|
||||||
// olmSessionData is a serialized OlmSession
|
|
||||||
internal open class OlmSessionEntity(
|
|
||||||
@PrimaryKey var primaryKey: String = "",
|
|
||||||
var sessionId: String? = null,
|
|
||||||
var deviceKey: String? = null,
|
|
||||||
var olmSessionData: String? = null,
|
|
||||||
var lastReceivedMessageTs: Long = 0
|
|
||||||
) :
|
|
||||||
RealmObject() {
|
|
||||||
|
|
||||||
fun getOlmSession(): OlmSession? {
|
|
||||||
return deserializeFromRealm(olmSessionData)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun putOlmSession(olmSession: OlmSession?) {
|
|
||||||
olmSessionData = serializeForRealm(olmSession)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object
|
|
||||||
}
|
|
|
@ -1,45 +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.model
|
|
||||||
|
|
||||||
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(
|
|
||||||
var serializedOutboundSessionData: String? = null,
|
|
||||||
var creationTime: Long? = null,
|
|
||||||
var shouldShareHistory: Boolean = false
|
|
||||||
) : 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
|
|
||||||
}
|
|
|
@ -1,136 +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.model
|
|
||||||
|
|
||||||
import com.squareup.moshi.JsonAdapter
|
|
||||||
import com.squareup.moshi.Types
|
|
||||||
import io.realm.RealmList
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.annotations.Index
|
|
||||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.OutgoingKeyRequest
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.OutgoingRoomKeyRequestState
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.RequestReply
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.RequestResult
|
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.RoomKeyRequestBody
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.Event
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.EventType
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.toModel
|
|
||||||
import org.matrix.android.sdk.internal.di.MoshiProvider
|
|
||||||
|
|
||||||
internal open class OutgoingKeyRequestEntity(
|
|
||||||
@Index var requestId: String? = null,
|
|
||||||
var requestedIndex: Int? = null,
|
|
||||||
var recipientsData: String? = null,
|
|
||||||
var requestedInfoStr: String? = null,
|
|
||||||
var creationTimeStamp: Long? = null,
|
|
||||||
// de-normalization for better query (if not have to query all and parse json)
|
|
||||||
@Index var roomId: String? = null,
|
|
||||||
@Index var megolmSessionId: String? = null,
|
|
||||||
|
|
||||||
var replies: RealmList<KeyRequestReplyEntity> = RealmList()
|
|
||||||
) : RealmObject() {
|
|
||||||
|
|
||||||
@Index private var requestStateStr: String = OutgoingRoomKeyRequestState.UNSENT.name
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private val recipientsDataMapper: JsonAdapter<Map<String, List<String>>> =
|
|
||||||
MoshiProvider
|
|
||||||
.providesMoshi()
|
|
||||||
.adapter(
|
|
||||||
Types.newParameterizedType(Map::class.java, String::class.java, List::class.java)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRequestedKeyInfo(): RoomKeyRequestBody? = RoomKeyRequestBody.fromJson(requestedInfoStr)
|
|
||||||
|
|
||||||
fun setRequestBody(body: RoomKeyRequestBody) {
|
|
||||||
requestedInfoStr = body.toJson()
|
|
||||||
roomId = body.roomId
|
|
||||||
megolmSessionId = body.sessionId
|
|
||||||
}
|
|
||||||
|
|
||||||
var requestState: OutgoingRoomKeyRequestState
|
|
||||||
get() {
|
|
||||||
return tryOrNull { OutgoingRoomKeyRequestState.valueOf(requestStateStr) }
|
|
||||||
?: OutgoingRoomKeyRequestState.UNSENT
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
requestStateStr = value.name
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getRecipients(): Map<String, List<String>>? {
|
|
||||||
return this.recipientsData?.let { recipientsDataMapper.fromJson(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setRecipients(recipients: Map<String, List<String>>) {
|
|
||||||
this.recipientsData = recipientsDataMapper.toJson(recipients)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addReply(userId: String, fromDevice: String?, event: Event) {
|
|
||||||
val newReply = KeyRequestReplyEntity(
|
|
||||||
senderId = userId,
|
|
||||||
fromDevice = fromDevice,
|
|
||||||
eventJson = MoshiProvider.providesMoshi().adapter(Event::class.java).toJson(event)
|
|
||||||
)
|
|
||||||
replies.add(newReply)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun toOutgoingKeyRequest(): OutgoingKeyRequest {
|
|
||||||
return OutgoingKeyRequest(
|
|
||||||
requestBody = getRequestedKeyInfo(),
|
|
||||||
recipients = getRecipients().orEmpty(),
|
|
||||||
requestId = requestId ?: "",
|
|
||||||
fromIndex = requestedIndex ?: 0,
|
|
||||||
state = requestState,
|
|
||||||
results = replies.mapNotNull { entity ->
|
|
||||||
val userId = entity.senderId ?: return@mapNotNull null
|
|
||||||
val result = entity.eventJson?.let {
|
|
||||||
MoshiProvider.providesMoshi().adapter(Event::class.java).fromJson(it)
|
|
||||||
}?.let { event ->
|
|
||||||
eventToResult(event)
|
|
||||||
} ?: return@mapNotNull null
|
|
||||||
RequestReply(
|
|
||||||
userId = userId,
|
|
||||||
fromDevice = entity.fromDevice,
|
|
||||||
result = result
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun eventToResult(event: Event): RequestResult? {
|
|
||||||
return when (event.getClearType()) {
|
|
||||||
in EventType.ROOM_KEY_WITHHELD.values -> {
|
|
||||||
event.content.toModel<RoomKeyWithHeldContent>()?.code?.let {
|
|
||||||
RequestResult.Failure(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
EventType.FORWARDED_ROOM_KEY -> {
|
|
||||||
RequestResult.Success((event.content?.get("chain_index") as? Number)?.toInt() ?: 0)
|
|
||||||
}
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun OutgoingKeyRequestEntity.deleteOnCascade() {
|
|
||||||
replies.deleteAllFromRealm()
|
|
||||||
deleteFromRealm()
|
|
||||||
}
|
|
|
@ -1,38 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.annotations.Index
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keep a record of to whom (user/device) a given session should have been shared.
|
|
||||||
* It will be used to reply to keyshare requests from other users, in order to see if
|
|
||||||
* this session was originaly shared with a given user
|
|
||||||
*/
|
|
||||||
internal open class SharedSessionEntity(
|
|
||||||
var roomId: String? = null,
|
|
||||||
var algorithm: String? = null,
|
|
||||||
@Index var sessionId: String? = null,
|
|
||||||
@Index var userId: String? = null,
|
|
||||||
@Index var deviceId: String? = null,
|
|
||||||
@Index var deviceIdentityKey: String? = null,
|
|
||||||
var chainIndex: Int? = null
|
|
||||||
) : RealmObject() {
|
|
||||||
|
|
||||||
companion object
|
|
||||||
}
|
|
|
@ -1,38 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmList
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.annotations.PrimaryKey
|
|
||||||
import org.matrix.android.sdk.internal.extensions.clearWith
|
|
||||||
|
|
||||||
internal open class UserEntity(
|
|
||||||
@PrimaryKey var userId: String? = null,
|
|
||||||
var devices: RealmList<DeviceInfoEntity> = RealmList(),
|
|
||||||
var crossSigningInfoEntity: CrossSigningInfoEntity? = null,
|
|
||||||
var deviceTrackingStatus: Int = 0
|
|
||||||
) : RealmObject() {
|
|
||||||
|
|
||||||
companion object
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun UserEntity.deleteOnCascade() {
|
|
||||||
devices.clearWith { it.deleteOnCascade() }
|
|
||||||
crossSigningInfoEntity?.deleteOnCascade()
|
|
||||||
deleteFromRealm()
|
|
||||||
}
|
|
|
@ -1,48 +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.model
|
|
||||||
|
|
||||||
import io.realm.RealmObject
|
|
||||||
import io.realm.annotations.Index
|
|
||||||
import org.matrix.android.sdk.api.session.events.model.content.WithHeldCode
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When an encrypted message is sent in a room, the megolm key might not be sent to all devices present in the room.
|
|
||||||
* Sometimes this may be inadvertent (for example, if the sending device is not aware of some devices that have joined),
|
|
||||||
* but some times, this may be purposeful.
|
|
||||||
* For example, the sender may have blacklisted certain devices or users,
|
|
||||||
* or may be choosing to not send the megolm key to devices that they have not verified yet.
|
|
||||||
*/
|
|
||||||
internal open class WithHeldSessionEntity(
|
|
||||||
var roomId: String? = null,
|
|
||||||
var algorithm: String? = null,
|
|
||||||
@Index var sessionId: String? = null,
|
|
||||||
@Index var senderKey: String? = null,
|
|
||||||
var codeString: String? = null,
|
|
||||||
var reason: String? = null
|
|
||||||
) : RealmObject() {
|
|
||||||
|
|
||||||
var code: WithHeldCode?
|
|
||||||
get() {
|
|
||||||
return WithHeldCode.fromCode(codeString)
|
|
||||||
}
|
|
||||||
set(code) {
|
|
||||||
codeString = code?.value
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object
|
|
||||||
}
|
|
|
@ -1,36 +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.query
|
|
||||||
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.CrossSigningInfoEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.UserEntityFields
|
|
||||||
|
|
||||||
internal fun CrossSigningInfoEntity.Companion.getOrCreate(realm: Realm, userId: String): CrossSigningInfoEntity {
|
|
||||||
return realm.where<CrossSigningInfoEntity>()
|
|
||||||
.equalTo(UserEntityFields.USER_ID, userId)
|
|
||||||
.findFirst()
|
|
||||||
?: realm.createObject(userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun CrossSigningInfoEntity.Companion.get(realm: Realm, userId: String): CrossSigningInfoEntity? {
|
|
||||||
return realm.where<CrossSigningInfoEntity>()
|
|
||||||
.equalTo(UserEntityFields.USER_ID, userId)
|
|
||||||
.findFirst()
|
|
||||||
}
|
|
|
@ -1,39 +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.query
|
|
||||||
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.DeviceInfoEntityFields
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.createPrimaryKey
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or create a device info.
|
|
||||||
*/
|
|
||||||
internal fun DeviceInfoEntity.Companion.getOrCreate(realm: Realm, userId: String, deviceId: String): DeviceInfoEntity {
|
|
||||||
val key = DeviceInfoEntity.createPrimaryKey(userId, deviceId)
|
|
||||||
|
|
||||||
return realm.where<DeviceInfoEntity>()
|
|
||||||
.equalTo(DeviceInfoEntityFields.PRIMARY_KEY, key)
|
|
||||||
.findFirst()
|
|
||||||
?: realm.createObject<DeviceInfoEntity>(key)
|
|
||||||
.apply {
|
|
||||||
this.deviceId = deviceId
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,71 +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.query
|
|
||||||
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmResults
|
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntityFields
|
|
||||||
|
|
||||||
internal fun SharedSessionEntity.Companion.get(
|
|
||||||
realm: Realm,
|
|
||||||
roomId: String?,
|
|
||||||
sessionId: String,
|
|
||||||
userId: String,
|
|
||||||
deviceId: String,
|
|
||||||
deviceIdentityKey: String?
|
|
||||||
): SharedSessionEntity? {
|
|
||||||
return realm.where<SharedSessionEntity>()
|
|
||||||
.equalTo(SharedSessionEntityFields.ROOM_ID, roomId)
|
|
||||||
.equalTo(SharedSessionEntityFields.SESSION_ID, sessionId)
|
|
||||||
.equalTo(SharedSessionEntityFields.ALGORITHM, MXCRYPTO_ALGORITHM_MEGOLM)
|
|
||||||
.equalTo(SharedSessionEntityFields.USER_ID, userId)
|
|
||||||
.equalTo(SharedSessionEntityFields.DEVICE_ID, deviceId)
|
|
||||||
.equalTo(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, deviceIdentityKey)
|
|
||||||
.findFirst()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, sessionId: String): RealmResults<SharedSessionEntity> {
|
|
||||||
return realm.where<SharedSessionEntity>()
|
|
||||||
.equalTo(SharedSessionEntityFields.ROOM_ID, roomId)
|
|
||||||
.equalTo(SharedSessionEntityFields.SESSION_ID, sessionId)
|
|
||||||
.equalTo(SharedSessionEntityFields.ALGORITHM, MXCRYPTO_ALGORITHM_MEGOLM)
|
|
||||||
.findAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun SharedSessionEntity.Companion.create(
|
|
||||||
realm: Realm,
|
|
||||||
roomId: String?,
|
|
||||||
sessionId: String,
|
|
||||||
userId: String,
|
|
||||||
deviceId: String,
|
|
||||||
deviceIdentityKey: String,
|
|
||||||
chainIndex: Int
|
|
||||||
): SharedSessionEntity {
|
|
||||||
return realm.createObject<SharedSessionEntity>().apply {
|
|
||||||
this.roomId = roomId
|
|
||||||
this.algorithm = MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
this.sessionId = sessionId
|
|
||||||
this.userId = userId
|
|
||||||
this.deviceId = deviceId
|
|
||||||
this.deviceIdentityKey = deviceIdentityKey
|
|
||||||
this.chainIndex = chainIndex
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,44 +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.query
|
|
||||||
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
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.crypto.store.db.model.deleteOnCascade
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get or create a user.
|
|
||||||
*/
|
|
||||||
internal fun UserEntity.Companion.getOrCreate(realm: Realm, userId: String): UserEntity {
|
|
||||||
return realm.where<UserEntity>()
|
|
||||||
.equalTo(UserEntityFields.USER_ID, userId)
|
|
||||||
.findFirst()
|
|
||||||
?: realm.createObject(userId)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a user.
|
|
||||||
*/
|
|
||||||
internal fun UserEntity.Companion.delete(realm: Realm, userId: String) {
|
|
||||||
realm.where<UserEntity>()
|
|
||||||
.equalTo(UserEntityFields.USER_ID, userId)
|
|
||||||
.findFirst()
|
|
||||||
?.deleteOnCascade()
|
|
||||||
}
|
|
|
@ -1,41 +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.query
|
|
||||||
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.kotlin.createObject
|
|
||||||
import io.realm.kotlin.where
|
|
||||||
import org.matrix.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity
|
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntityFields
|
|
||||||
|
|
||||||
internal fun WithHeldSessionEntity.Companion.get(realm: Realm, roomId: String, sessionId: String): WithHeldSessionEntity? {
|
|
||||||
return realm.where<WithHeldSessionEntity>()
|
|
||||||
.equalTo(WithHeldSessionEntityFields.ROOM_ID, roomId)
|
|
||||||
.equalTo(WithHeldSessionEntityFields.SESSION_ID, sessionId)
|
|
||||||
.equalTo(WithHeldSessionEntityFields.ALGORITHM, MXCRYPTO_ALGORITHM_MEGOLM)
|
|
||||||
.findFirst()
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun WithHeldSessionEntity.Companion.getOrCreate(realm: Realm, roomId: String, sessionId: String): WithHeldSessionEntity? {
|
|
||||||
return get(realm, roomId, sessionId)
|
|
||||||
?: realm.createObject<WithHeldSessionEntity>().apply {
|
|
||||||
this.roomId = roomId
|
|
||||||
this.algorithm = MXCRYPTO_ALGORITHM_MEGOLM
|
|
||||||
this.sessionId = sessionId
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,58 +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.tools
|
|
||||||
|
|
||||||
import org.matrix.olm.OlmPkDecryption
|
|
||||||
import org.matrix.olm.OlmPkEncryption
|
|
||||||
import org.matrix.olm.OlmPkSigning
|
|
||||||
import org.matrix.olm.OlmUtility
|
|
||||||
|
|
||||||
internal fun <T> withOlmEncryption(block: (OlmPkEncryption) -> T): T {
|
|
||||||
val olmPkEncryption = OlmPkEncryption()
|
|
||||||
try {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -34,6 +34,7 @@ import org.matrix.rustcomponents.sdk.crypto.CryptoStoreException
|
||||||
import org.matrix.rustcomponents.sdk.crypto.Sas
|
import org.matrix.rustcomponents.sdk.crypto.Sas
|
||||||
import org.matrix.rustcomponents.sdk.crypto.SasListener
|
import org.matrix.rustcomponents.sdk.crypto.SasListener
|
||||||
import org.matrix.rustcomponents.sdk.crypto.SasState
|
import org.matrix.rustcomponents.sdk.crypto.SasState
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
/** Class representing a short auth string verification flow. */
|
/** Class representing a short auth string verification flow. */
|
||||||
internal class SasVerification @AssistedInject constructor(
|
internal class SasVerification @AssistedInject constructor(
|
||||||
|
@ -72,6 +73,11 @@ internal class SasVerification @AssistedInject constructor(
|
||||||
|
|
||||||
override fun state(): SasTransactionState {
|
override fun state(): SasTransactionState {
|
||||||
return when (val state = innerState) {
|
return when (val state = innerState) {
|
||||||
|
SasState.Created -> {
|
||||||
|
// Note: this does not seem to be used, but emit a warning just in case.
|
||||||
|
Timber.w("SasState.Created received")
|
||||||
|
SasTransactionState.None
|
||||||
|
}
|
||||||
SasState.Started -> SasTransactionState.SasStarted
|
SasState.Started -> SasTransactionState.SasStarted
|
||||||
SasState.Accepted -> SasTransactionState.SasAccepted
|
SasState.Accepted -> SasTransactionState.SasAccepted
|
||||||
is SasState.KeysExchanged -> {
|
is SasState.KeysExchanged -> {
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,13 +19,14 @@ package org.matrix.android.sdk.internal.session.contentscanner
|
||||||
import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt
|
import org.matrix.android.sdk.api.session.crypto.attachments.ElementToDecrypt
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
|
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileInfo
|
||||||
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileKey
|
import org.matrix.android.sdk.api.session.crypto.model.EncryptedFileKey
|
||||||
import org.matrix.android.sdk.internal.crypto.tools.withOlmEncryption
|
|
||||||
import org.matrix.android.sdk.internal.session.contentscanner.model.DownloadBody
|
import org.matrix.android.sdk.internal.session.contentscanner.model.DownloadBody
|
||||||
import org.matrix.android.sdk.internal.session.contentscanner.model.EncryptedBody
|
import org.matrix.android.sdk.internal.session.contentscanner.model.EncryptedBody
|
||||||
import org.matrix.android.sdk.internal.session.contentscanner.model.toCanonicalJson
|
import org.matrix.android.sdk.internal.session.contentscanner.model.toCanonicalJson
|
||||||
|
import org.matrix.rustcomponents.sdk.crypto.PkEncryption
|
||||||
|
|
||||||
internal object ScanEncryptorUtils {
|
internal object ScanEncryptorUtils {
|
||||||
|
|
||||||
|
@Throws
|
||||||
fun getDownloadBodyAndEncryptIfNeeded(publicServerKey: String?, mxcUrl: String, elementToDecrypt: ElementToDecrypt): DownloadBody {
|
fun getDownloadBodyAndEncryptIfNeeded(publicServerKey: String?, mxcUrl: String, elementToDecrypt: ElementToDecrypt): DownloadBody {
|
||||||
// TODO, upstream refactoring changed the object model here...
|
// TODO, upstream refactoring changed the object model here...
|
||||||
// it's bad we have to recreate and use hardcoded values
|
// it's bad we have to recreate and use hardcoded values
|
||||||
|
@ -43,19 +44,18 @@ internal object ScanEncryptorUtils {
|
||||||
v = "v2"
|
v = "v2"
|
||||||
)
|
)
|
||||||
return if (publicServerKey != null) {
|
return if (publicServerKey != null) {
|
||||||
// We should encrypt
|
// Note: fromBase64 can throw Exception
|
||||||
withOlmEncryption { olm ->
|
val pkEncryption = PkEncryption.fromBase64(key = publicServerKey)
|
||||||
olm.setRecipientKey(publicServerKey)
|
val pkMessage = pkEncryption.use {
|
||||||
|
pkEncryption.encrypt(DownloadBody(encryptedInfo).toCanonicalJson())
|
||||||
val olmResult = olm.encrypt(DownloadBody(encryptedInfo).toCanonicalJson())
|
|
||||||
DownloadBody(
|
|
||||||
encryptedBody = EncryptedBody(
|
|
||||||
cipherText = olmResult.mCipherText,
|
|
||||||
ephemeral = olmResult.mEphemeralKey,
|
|
||||||
mac = olmResult.mMac
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
DownloadBody(
|
||||||
|
encryptedBody = EncryptedBody(
|
||||||
|
cipherText = pkMessage.ciphertext,
|
||||||
|
ephemeral = pkMessage.ephemeralKey,
|
||||||
|
mac = pkMessage.mac
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
DownloadBody(encryptedInfo)
|
DownloadBody(encryptedInfo)
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,12 +42,10 @@ internal class DefaultScanEncryptedTask @Inject constructor(
|
||||||
|
|
||||||
override suspend fun execute(params: ScanEncryptedTask.Params): ScanResponse {
|
override suspend fun execute(params: ScanEncryptedTask.Params): ScanResponse {
|
||||||
val mxcUrl = params.mxcUrl
|
val mxcUrl = params.mxcUrl
|
||||||
val dlBody = ScanEncryptorUtils.getDownloadBodyAndEncryptIfNeeded(params.publicServerKey, params.mxcUrl, params.encryptedInfo)
|
|
||||||
|
|
||||||
val scannerUrl = contentScannerStore.getScannerUrl()
|
val scannerUrl = contentScannerStore.getScannerUrl()
|
||||||
contentScannerStore.updateStateForContent(params.mxcUrl, ScanState.IN_PROGRESS, scannerUrl)
|
contentScannerStore.updateStateForContent(mxcUrl, ScanState.IN_PROGRESS, scannerUrl)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
val dlBody = ScanEncryptorUtils.getDownloadBodyAndEncryptIfNeeded(params.publicServerKey, mxcUrl, params.encryptedInfo)
|
||||||
val api = contentScannerApiProvider.contentScannerApi ?: throw IllegalArgumentException()
|
val api = contentScannerApiProvider.contentScannerApi ?: throw IllegalArgumentException()
|
||||||
val executeRequest = executeRequest<ScanResponse>(null) {
|
val executeRequest = executeRequest<ScanResponse>(null) {
|
||||||
api.scanFile(dlBody)
|
api.scanFile(dlBody)
|
||||||
|
@ -60,7 +58,7 @@ internal class DefaultScanEncryptedTask @Inject constructor(
|
||||||
)
|
)
|
||||||
return executeRequest
|
return executeRequest
|
||||||
} catch (failure: Throwable) {
|
} catch (failure: Throwable) {
|
||||||
contentScannerStore.updateStateForContent(params.mxcUrl, ScanState.UNKNOWN, scannerUrl)
|
contentScannerStore.updateStateForContent(mxcUrl, ScanState.UNKNOWN, scannerUrl)
|
||||||
throw failure.toScanFailure() ?: failure
|
throw failure.toScanFailure() ?: failure
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
@ -43,7 +42,8 @@ internal interface IdentityBulkLookupTask : Task<IdentityBulkLookupTask.Params,
|
||||||
internal class DefaultIdentityBulkLookupTask @Inject constructor(
|
internal class DefaultIdentityBulkLookupTask @Inject constructor(
|
||||||
private val identityApiProvider: IdentityApiProvider,
|
private val identityApiProvider: IdentityApiProvider,
|
||||||
private val identityStore: IdentityStore,
|
private val identityStore: IdentityStore,
|
||||||
@UserId private val userId: String
|
@UserId private val userId: String,
|
||||||
|
private val sha256Converter: Sha256Converter,
|
||||||
) : IdentityBulkLookupTask {
|
) : IdentityBulkLookupTask {
|
||||||
|
|
||||||
override suspend fun execute(params: IdentityBulkLookupTask.Params): List<FoundThreePid> {
|
override suspend fun execute(params: IdentityBulkLookupTask.Params): List<FoundThreePid> {
|
||||||
|
@ -118,15 +118,12 @@ 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(
|
sha256Converter.convertToSha256(
|
||||||
olmUtility.sha256(
|
str = threePid.value.lowercase(Locale.ROOT) + " " + threePid.toMedium() + " " + pepper
|
||||||
threePid.value.lowercase(Locale.ROOT) +
|
)
|
||||||
" " + threePid.toMedium() + " " + pepper
|
)
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2020 The Matrix.org Foundation C.I.C.
|
* Copyright (c) 2024 The Matrix.org Foundation C.I.C.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -14,16 +14,16 @@
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.matrix.android.sdk.internal.crypto.store.db.model
|
package org.matrix.android.sdk.internal.session.identity
|
||||||
|
|
||||||
import io.realm.RealmObject
|
import org.matrix.android.sdk.api.util.toBase64NoPadding
|
||||||
|
import java.security.MessageDigest
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
internal open class TrustLevelEntity(
|
class Sha256Converter @Inject constructor() {
|
||||||
var crossSignedVerified: Boolean? = null,
|
private val sha256 by lazy { MessageDigest.getInstance("SHA-256") }
|
||||||
var locallyVerified: Boolean? = null
|
|
||||||
) : RealmObject() {
|
|
||||||
|
|
||||||
companion object
|
fun convertToSha256(str: String): String {
|
||||||
|
return sha256.digest(str.toByteArray()).toBase64NoPadding()
|
||||||
fun isVerified(): Boolean = crossSignedVerified == true || locallyVerified == true
|
}
|
||||||
}
|
}
|
|
@ -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<>();
|
|
||||||
}
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
/*
|
||||||
|
* 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.session.identity
|
||||||
|
|
||||||
|
import org.amshove.kluent.shouldBeEqualTo
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
class Sha256Test {
|
||||||
|
/**
|
||||||
|
* Check that the behavior is the same than what is done in the Olm library.
|
||||||
|
* https://gitlab.matrix.org/matrix-org/olm/-/blob/master/tests/test_olm_sha256.cpp#L16
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
fun testSha256() {
|
||||||
|
val sut = Sha256Converter()
|
||||||
|
sut.convertToSha256("Hello, World") shouldBeEqualTo "A2daxT/5zRU1zMffzfosRYxSGDcfQY3BNvLRmsH76KU"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue