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
|
||||
implementation libs.androidx.work
|
||||
|
||||
// olm lib is now hosted in MavenCentral
|
||||
implementation 'org.matrix.android:olm-sdk:3.2.12'
|
||||
|
||||
// DI
|
||||
implementation libs.dagger.dagger
|
||||
kapt libs.dagger.daggerCompiler
|
||||
|
@ -224,7 +221,7 @@ dependencies {
|
|||
|
||||
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")
|
||||
|
||||
testImplementation libs.tests.junit
|
||||
|
@ -236,6 +233,7 @@ dependencies {
|
|||
testImplementation 'net.lachlanmckee:timber-junit-rule:1.0.1'
|
||||
// Transitively required for mocking realm as monarchy doesn't expose Rx
|
||||
testImplementation libs.rx.rxKotlin
|
||||
testImplementation libs.tests.robolectric
|
||||
|
||||
kaptAndroidTest libs.dagger.daggerCompiler
|
||||
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.util.BackgroundDetectionObserver
|
||||
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
||||
import org.matrix.olm.OlmManager
|
||||
import java.util.concurrent.Executors
|
||||
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 userAgentHolder: UserAgentHolder
|
||||
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
||||
@Inject internal lateinit var olmManager: OlmManager
|
||||
@Inject internal lateinit var sessionManager: SessionManager
|
||||
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
||||
@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.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Before
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
|
@ -29,19 +28,12 @@ import org.junit.runners.MethodSorters
|
|||
import org.matrix.android.sdk.InstrumentedTest
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
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")
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@FixMethodOrder(MethodSorters.JVM)
|
||||
class KeysBackupPasswordTest : InstrumentedTest {
|
||||
|
||||
@Before
|
||||
fun ensureLibLoaded() {
|
||||
OlmManager()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check KeysBackupPassword utilities
|
||||
*/
|
||||
|
@ -51,7 +43,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
|
||||
assertEquals(32, generatePrivateKeyResult.salt.length)
|
||||
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, generatePrivateKeyResult.privateKey.size)
|
||||
|
||||
// Reverse operation
|
||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
||||
|
@ -60,7 +52,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
generatePrivateKeyResult.iterations
|
||||
)
|
||||
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||
assertArrayEquals(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
||||
}
|
||||
|
||||
|
@ -101,7 +93,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
|
||||
assertEquals(32, generatePrivateKeyResult.salt.length)
|
||||
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, generatePrivateKeyResult.privateKey.size)
|
||||
|
||||
// Reverse operation, with bad password
|
||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
||||
|
@ -110,7 +102,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
generatePrivateKeyResult.iterations
|
||||
)
|
||||
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
||||
}
|
||||
|
||||
|
@ -123,7 +115,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
|
||||
assertEquals(32, generatePrivateKeyResult.salt.length)
|
||||
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, generatePrivateKeyResult.privateKey.size)
|
||||
|
||||
// Reverse operation, with bad iteration
|
||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
||||
|
@ -132,7 +124,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
500_001
|
||||
)
|
||||
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
||||
}
|
||||
|
||||
|
@ -145,7 +137,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
|
||||
assertEquals(32, generatePrivateKeyResult.salt.length)
|
||||
assertEquals(500_000, generatePrivateKeyResult.iterations)
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), generatePrivateKeyResult.privateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, generatePrivateKeyResult.privateKey.size)
|
||||
|
||||
// Reverse operation, with bad iteration
|
||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(
|
||||
|
@ -154,7 +146,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
generatePrivateKeyResult.iterations
|
||||
)
|
||||
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||
assertByteArrayNotEqual(generatePrivateKeyResult.privateKey, retrievedPrivateKey)
|
||||
}
|
||||
|
||||
|
@ -169,7 +161,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
|
||||
val retrievedPrivateKey = retrievePrivateKeyWithPassword(password, salt, iteration)
|
||||
|
||||
assertEquals(OlmPkDecryption.privateKeyLength(), retrievedPrivateKey.size)
|
||||
assertEquals(EXPECTED_PRIVATE_KEY_LENGTH, retrievedPrivateKey.size)
|
||||
|
||||
// Data from RiotWeb
|
||||
val privateKeyBytes = byteArrayOf(
|
||||
|
@ -187,5 +179,7 @@ class KeysBackupPasswordTest : InstrumentedTest {
|
|||
private const val BAD_PASSWORD = "passw0rd"
|
||||
|
||||
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
|
||||
|
||||
import okio.ByteString.Companion.decodeBase64
|
||||
import org.amshove.kluent.shouldBe
|
||||
import org.amshove.kluent.shouldBeEqualTo
|
||||
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.model.EncryptedFileInfo
|
||||
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.EncryptedBody
|
||||
import org.matrix.olm.OlmPkMessage
|
||||
|
||||
class ScanEncryptorUtilsTest {
|
||||
private val anMxcUrl = "mxc://matrix.org/123456"
|
||||
|
@ -67,7 +62,6 @@ class ScanEncryptorUtilsTest {
|
|||
|
||||
@Test
|
||||
fun whenServerKeyIsProvidedTheContentIsEncrypted() {
|
||||
System.loadLibrary("olm")
|
||||
val result = ScanEncryptorUtils.getDownloadBodyAndEncryptIfNeeded(
|
||||
publicServerKey = aPublicKey,
|
||||
mxcUrl = anMxcUrl,
|
||||
|
@ -78,6 +72,8 @@ class ScanEncryptorUtilsTest {
|
|||
result.encryptedBody shouldNotBe null
|
||||
}
|
||||
|
||||
// Note: PkDecryption is not exposed in the FFI layer, so we cannot use this test.
|
||||
/*
|
||||
@Test
|
||||
fun checkThatTheCodeIsAbleToDecryptContent() {
|
||||
System.loadLibrary("olm")
|
||||
|
@ -121,4 +117,5 @@ class ScanEncryptorUtilsTest {
|
|||
.fromJson(result)
|
||||
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.util.BackgroundDetectionObserver
|
||||
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
||||
import org.matrix.olm.OlmManager
|
||||
import java.util.concurrent.Executors
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -59,7 +58,6 @@ class Matrix(context: Context, matrixConfiguration: MatrixConfiguration) {
|
|||
@Inject internal lateinit var debugService: DebugService
|
||||
@Inject internal lateinit var userAgentHolder: UserAgentHolder
|
||||
@Inject internal lateinit var backgroundDetectionObserver: BackgroundDetectionObserver
|
||||
@Inject internal lateinit var olmManager: OlmManager
|
||||
@Inject internal lateinit var sessionManager: SessionManager
|
||||
@Inject internal lateinit var homeServerHistoryService: HomeServerHistoryService
|
||||
@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"
|
||||
|
||||
/**
|
||||
* 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. **/
|
||||
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.MXUsersDevicesMap
|
||||
import org.matrix.olm.OlmException
|
||||
|
||||
/**
|
||||
* Represents a crypto error response.
|
||||
|
@ -34,8 +33,6 @@ sealed class MXCryptoError : Throwable() {
|
|||
val detailedErrorDescription: String? = null
|
||||
) : MXCryptoError()
|
||||
|
||||
data class OlmError(val olmException: OlmException) : MXCryptoError()
|
||||
|
||||
data class UnknownDevice(val deviceList: MXUsersDevicesMap<CryptoDeviceInfo>) : MXCryptoError()
|
||||
|
||||
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.crypto.keysbackup.model.rest.DefaultKeysAlgorithmAndData
|
||||
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.verification.SasVerification
|
||||
import org.matrix.android.sdk.internal.crypto.verification.VerificationRequest
|
||||
|
@ -318,22 +317,6 @@ internal class OlmMachine @Inject constructor(
|
|||
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.
|
||||
*
|
||||
|
|
|
@ -121,7 +121,8 @@ internal class PrepareToEncryptUseCase @Inject constructor(
|
|||
HistoryVisibility.INVITED
|
||||
} else {
|
||||
HistoryVisibility.JOINED
|
||||
}
|
||||
},
|
||||
errorOnVerifiedUserProblem = false,
|
||||
)
|
||||
measureTimeMillis {
|
||||
keyShareLock.withLock {
|
||||
|
|
|
@ -504,15 +504,8 @@ internal class RustCryptoService @Inject constructor(
|
|||
val content = event.content?.toModel<EncryptedEventContent>() ?: throw mxCryptoError
|
||||
val roomId = event.roomId
|
||||
val sessionId = content.sessionId
|
||||
val senderKey = content.senderKey
|
||||
if (roomId != null && sessionId != null) {
|
||||
// try to perform a lazy migration from legacy store
|
||||
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)
|
||||
}
|
||||
perSessionBackupQueryRateLimiter.tryFromBackupIfPossible(sessionId, roomId)
|
||||
}
|
||||
}
|
||||
throw mxCryptoError
|
||||
|
@ -851,9 +844,9 @@ internal class RustCryptoService @Inject constructor(
|
|||
override fun removeSessionListener(listener: NewSessionListener) {
|
||||
megolmSessionImportManager.removeListener(listener)
|
||||
}
|
||||
/* ==========================================================================================
|
||||
* DEBUG INFO
|
||||
* ========================================================================================== */
|
||||
/* ==========================================================================================
|
||||
* DEBUG INFO
|
||||
* ========================================================================================== */
|
||||
|
||||
override fun toString(): String {
|
||||
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.session.SessionScope
|
||||
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.RequestType
|
||||
import org.matrix.rustcomponents.sdk.crypto.SignatureVerification
|
||||
|
@ -840,8 +839,8 @@ internal class RustKeyBackupService @Inject constructor(
|
|||
try {
|
||||
olmMachine.enableBackupV1(retrievedMegolmBackupAuthData.publicKey, keysVersionResult.version)
|
||||
keysBackupVersion = keysVersionResult
|
||||
} catch (e: OlmException) {
|
||||
Timber.e(e, "OlmException")
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e, "Exception")
|
||||
keysBackupStateManager.state = KeysBackupState.Disabled
|
||||
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 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_CURVE25519_AES_SHA2
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.listeners.ProgressListener
|
||||
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.keysbackup.generatePrivateKeyWithPassword
|
||||
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.olm.OlmPkMessage
|
||||
import java.security.SecureRandom
|
||||
import javax.crypto.Cipher
|
||||
import javax.crypto.Mac
|
||||
|
@ -321,22 +318,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
|||
?: throw SharedSecretStorageError.ParsingError
|
||||
|
||||
val algorithm = key.keyInfo.content
|
||||
if (SSSS_ALGORITHM_CURVE25519_AES_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) {
|
||||
if (SSSS_ALGORITHM_AES_HMAC_SHA2 == algorithm.algorithm) {
|
||||
val keySpec = secretKey as? RawBytesKeySpec ?: throw SharedSecretStorageError.BadKeyFormat
|
||||
return withContext(cryptoCoroutineScope.coroutineContext + coroutineDispatchers.computation) {
|
||||
decryptAesHmacSha2(keySpec, name, secretContent)
|
||||
|
@ -366,8 +348,7 @@ internal class DefaultSharedSecretStorageService @Inject constructor(
|
|||
val keyInfo = (keyInfoResult as? KeyInfoResult.Success)?.keyInfo
|
||||
?: return IntegrityResult.Error(SharedSecretStorageError.UnknownKey(keyId ?: ""))
|
||||
|
||||
if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2 &&
|
||||
keyInfo.content.algorithm != SSSS_ALGORITHM_CURVE25519_AES_SHA2) {
|
||||
if (keyInfo.content.algorithm != SSSS_ALGORITHM_AES_HMAC_SHA2) {
|
||||
// Unsupported algorithm
|
||||
return IntegrityResult.Error(
|
||||
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.events.model.content.EncryptionEventContent
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -143,14 +142,4 @@ interface IMXCommonCryptoStore {
|
|||
* @return the device or null if not found
|
||||
*/
|
||||
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.toOptional
|
||||
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.doRealmTransaction
|
||||
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.mapper.CryptoRoomInfoMapper
|
||||
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.CryptoRoomEntity
|
||||
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.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.getOrCreate
|
||||
import org.matrix.android.sdk.internal.di.CryptoDatabase
|
||||
import org.matrix.android.sdk.internal.di.DeviceId
|
||||
import org.matrix.android.sdk.internal.di.UserId
|
||||
import org.matrix.android.sdk.internal.session.SessionScope
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import timber.log.Timber
|
||||
import java.util.concurrent.Executors
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -75,7 +66,6 @@ private val loggerTag = LoggerTag("RealmCryptoStore", LoggerTag.CRYPTO)
|
|||
@SessionScope
|
||||
internal class RustCryptoStore @Inject constructor(
|
||||
@CryptoDatabase private val realmConfiguration: RealmConfiguration,
|
||||
private val clock: Clock,
|
||||
@UserId private val userId: String,
|
||||
@DeviceId private val deviceId: String,
|
||||
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
|
||||
// ================================================
|
||||
|
@ -163,30 +139,7 @@ internal class RustCryptoStore @Inject constructor(
|
|||
// nop
|
||||
}
|
||||
|
||||
override fun tidyUpDataBase() {
|
||||
// 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 tidyUpDataBase() = Unit
|
||||
|
||||
override fun close() {
|
||||
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
|
||||
|
||||
import android.util.Base64
|
||||
import io.realm.Realm
|
||||
import io.realm.RealmConfiguration
|
||||
import io.realm.RealmObject
|
||||
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
|
||||
|
||||
/**
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -70,38 +46,3 @@ internal fun doRealmTransactionAsync(realmConfiguration: RealmConfiguration, act
|
|||
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
|
||||
|
||||
import io.realm.DynamicRealm
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo001Legacy
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo002Legacy
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo003RiotX
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo004
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo005
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo006
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo007
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo008
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo009
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo010
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo011
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo012
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo013
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo014
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo015
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo016
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo017
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo018
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo019
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo020
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo021
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo022
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo023
|
||||
import org.matrix.android.sdk.internal.crypto.store.db.migration.MigrateCryptoTo024
|
||||
import org.matrix.android.sdk.internal.util.database.MatrixRealmMigration
|
||||
import org.matrix.android.sdk.internal.util.time.Clock
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
|
@ -49,13 +26,11 @@ import javax.inject.Inject
|
|||
* 0, 1, 2: legacy Riot-Android;
|
||||
* 3: migrate to RiotX schema;
|
||||
* 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(
|
||||
private val clock: Clock,
|
||||
private val rustMigrationInfoProvider: RustMigrationInfoProvider,
|
||||
) : MatrixRealmMigration(
|
||||
internal class RealmCryptoStoreMigration @Inject constructor() : MatrixRealmMigration(
|
||||
dbName = "Crypto",
|
||||
schemaVersion = 23L,
|
||||
schemaVersion = 24L,
|
||||
) {
|
||||
/**
|
||||
* Forces all RealmCryptoStoreMigration instances to be equal.
|
||||
|
@ -65,33 +40,6 @@ internal class RealmCryptoStoreMigration @Inject constructor(
|
|||
override fun hashCode() = 5000
|
||||
|
||||
override fun doMigrate(realm: DynamicRealm, oldVersion: Long) {
|
||||
if (oldVersion < 1) MigrateCryptoTo001Legacy(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()
|
||||
if (oldVersion < 24) MigrateCryptoTo024(realm).perform()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,23 +17,9 @@
|
|||
package org.matrix.android.sdk.internal.crypto.store.db
|
||||
|
||||
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.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.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.
|
||||
|
@ -43,21 +29,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEnti
|
|||
classes = [
|
||||
CryptoMetadataEntity::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,
|
||||
WithHeldSessionEntity::class,
|
||||
SharedSessionEntity::class,
|
||||
OutboundGroupSessionInfoEntity::class
|
||||
]
|
||||
)
|
||||
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.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(
|
||||
// The current user id.
|
||||
|
@ -53,15 +50,4 @@ internal open class CryptoMetadataEntity(
|
|||
var keyBackupRecoveryKeyVersion: String? = null
|
||||
|
||||
// var crossSigningInfoEntity: CrossSigningInfoEntity? = null
|
||||
) : RealmObject() {
|
||||
|
||||
// Deserialize data
|
||||
fun getOlmAccount(): OlmAccount? {
|
||||
return deserializeFromRealm(olmAccountData)
|
||||
}
|
||||
|
||||
// Serialize data
|
||||
fun putOlmAccount(olmAccount: OlmAccount?) {
|
||||
olmAccountData = serializeForRealm(olmAccount)
|
||||
}
|
||||
}
|
||||
) : RealmObject()
|
||||
|
|
|
@ -26,10 +26,6 @@ internal open class CryptoRoomEntity(
|
|||
var blacklistUnverifiedDevices: Boolean = false,
|
||||
// Determines whether or not room history should be shared on new member invites
|
||||
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
|
||||
// even if a new state event with empty encryption, or state is reset somehow
|
||||
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.SasListener
|
||||
import org.matrix.rustcomponents.sdk.crypto.SasState
|
||||
import timber.log.Timber
|
||||
|
||||
/** Class representing a short auth string verification flow. */
|
||||
internal class SasVerification @AssistedInject constructor(
|
||||
|
@ -72,6 +73,11 @@ internal class SasVerification @AssistedInject constructor(
|
|||
|
||||
override fun state(): SasTransactionState {
|
||||
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.Accepted -> SasTransactionState.SasAccepted
|
||||
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.system.SystemModule
|
||||
import org.matrix.android.sdk.internal.worker.MatrixWorkerFactory
|
||||
import org.matrix.olm.OlmManager
|
||||
import java.io.File
|
||||
|
||||
@Component(
|
||||
|
@ -89,8 +88,6 @@ internal interface MatrixComponent {
|
|||
@CacheDirectory
|
||||
fun cacheDir(): File
|
||||
|
||||
fun olmManager(): OlmManager
|
||||
|
||||
fun taskExecutor(): TaskExecutor
|
||||
|
||||
fun sessionParamsStore(): SessionParamsStore
|
||||
|
|
|
@ -25,7 +25,6 @@ import kotlinx.coroutines.android.asCoroutineDispatcher
|
|||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
|
||||
import org.matrix.android.sdk.internal.util.createBackgroundHandler
|
||||
import org.matrix.olm.OlmManager
|
||||
import java.io.File
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
|
@ -57,11 +56,4 @@ internal object MatrixModule {
|
|||
fun providesCacheDir(context: Context): File {
|
||||
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.model.EncryptedFileInfo
|
||||
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.EncryptedBody
|
||||
import org.matrix.android.sdk.internal.session.contentscanner.model.toCanonicalJson
|
||||
import org.matrix.rustcomponents.sdk.crypto.PkEncryption
|
||||
|
||||
internal object ScanEncryptorUtils {
|
||||
|
||||
@Throws
|
||||
fun getDownloadBodyAndEncryptIfNeeded(publicServerKey: String?, mxcUrl: String, elementToDecrypt: ElementToDecrypt): DownloadBody {
|
||||
// TODO, upstream refactoring changed the object model here...
|
||||
// it's bad we have to recreate and use hardcoded values
|
||||
|
@ -43,19 +44,18 @@ internal object ScanEncryptorUtils {
|
|||
v = "v2"
|
||||
)
|
||||
return if (publicServerKey != null) {
|
||||
// We should encrypt
|
||||
withOlmEncryption { olm ->
|
||||
olm.setRecipientKey(publicServerKey)
|
||||
|
||||
val olmResult = olm.encrypt(DownloadBody(encryptedInfo).toCanonicalJson())
|
||||
DownloadBody(
|
||||
encryptedBody = EncryptedBody(
|
||||
cipherText = olmResult.mCipherText,
|
||||
ephemeral = olmResult.mEphemeralKey,
|
||||
mac = olmResult.mMac
|
||||
)
|
||||
)
|
||||
// Note: fromBase64 can throw Exception
|
||||
val pkEncryption = PkEncryption.fromBase64(key = publicServerKey)
|
||||
val pkMessage = pkEncryption.use {
|
||||
pkEncryption.encrypt(DownloadBody(encryptedInfo).toCanonicalJson())
|
||||
}
|
||||
DownloadBody(
|
||||
encryptedBody = EncryptedBody(
|
||||
cipherText = pkMessage.ciphertext,
|
||||
ephemeral = pkMessage.ephemeralKey,
|
||||
mac = pkMessage.mac
|
||||
)
|
||||
)
|
||||
} else {
|
||||
DownloadBody(encryptedInfo)
|
||||
}
|
||||
|
|
|
@ -42,12 +42,10 @@ internal class DefaultScanEncryptedTask @Inject constructor(
|
|||
|
||||
override suspend fun execute(params: ScanEncryptedTask.Params): ScanResponse {
|
||||
val mxcUrl = params.mxcUrl
|
||||
val dlBody = ScanEncryptorUtils.getDownloadBodyAndEncryptIfNeeded(params.publicServerKey, params.mxcUrl, params.encryptedInfo)
|
||||
|
||||
val scannerUrl = contentScannerStore.getScannerUrl()
|
||||
contentScannerStore.updateStateForContent(params.mxcUrl, ScanState.IN_PROGRESS, scannerUrl)
|
||||
|
||||
contentScannerStore.updateStateForContent(mxcUrl, ScanState.IN_PROGRESS, scannerUrl)
|
||||
try {
|
||||
val dlBody = ScanEncryptorUtils.getDownloadBodyAndEncryptIfNeeded(params.publicServerKey, mxcUrl, params.encryptedInfo)
|
||||
val api = contentScannerApiProvider.contentScannerApi ?: throw IllegalArgumentException()
|
||||
val executeRequest = executeRequest<ScanResponse>(null) {
|
||||
api.scanFile(dlBody)
|
||||
|
@ -60,7 +58,7 @@ internal class DefaultScanEncryptedTask @Inject constructor(
|
|||
)
|
||||
return executeRequest
|
||||
} catch (failure: Throwable) {
|
||||
contentScannerStore.updateStateForContent(params.mxcUrl, ScanState.UNKNOWN, scannerUrl)
|
||||
contentScannerStore.updateStateForContent(mxcUrl, ScanState.UNKNOWN, scannerUrl)
|
||||
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.ThreePid
|
||||
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.network.executeRequest
|
||||
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(
|
||||
private val identityApiProvider: IdentityApiProvider,
|
||||
private val identityStore: IdentityStore,
|
||||
@UserId private val userId: String
|
||||
@UserId private val userId: String,
|
||||
private val sha256Converter: Sha256Converter,
|
||||
) : IdentityBulkLookupTask {
|
||||
|
||||
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> {
|
||||
return withOlmUtility { olmUtility ->
|
||||
threePids.map { threePid ->
|
||||
base64ToBase64Url(
|
||||
olmUtility.sha256(
|
||||
threePid.value.lowercase(Locale.ROOT) +
|
||||
" " + threePid.toMedium() + " " + pepper
|
||||
)
|
||||
)
|
||||
}
|
||||
return threePids.map { threePid ->
|
||||
base64ToBase64Url(
|
||||
sha256Converter.convertToSha256(
|
||||
str = 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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -14,16 +14,16 @@
|
|||
* 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(
|
||||
var crossSignedVerified: Boolean? = null,
|
||||
var locallyVerified: Boolean? = null
|
||||
) : RealmObject() {
|
||||
class Sha256Converter @Inject constructor() {
|
||||
private val sha256 by lazy { MessageDigest.getInstance("SHA-256") }
|
||||
|
||||
companion object
|
||||
|
||||
fun isVerified(): Boolean = crossSignedVerified == true || locallyVerified == true
|
||||
fun convertToSha256(str: String): String {
|
||||
return sha256.digest(str.toByteArray()).toBase64NoPadding()
|
||||
}
|
||||
}
|
|
@ -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