diff --git a/.gitattributes b/.gitattributes index 0542767eff..55bf9e73e4 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ **/snapshots/**/*.png filter=lfs diff=lfs merge=lfs -text +*.realm filter=lfs diff=lfs merge=lfs -text diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index fb6d773da5..5a0345e318 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -125,25 +125,6 @@ android { java.srcDirs += "src/sharedTest/java" } } -// -// flavorDimensions "crypto" -// -// productFlavors { -// kotlinCrypto { -// dimension "crypto" -// isDefault = true -// // versionName "${versionMajor}.${versionMinor}.${versionPatch}${getFdroidVersionSuffix()}" -// buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"JC\"" -// buildConfigField "String", "FLAVOR_DESCRIPTION", "\"KotlinCrypto\"" -// } -// rustCrypto { -// dimension "crypto" -// // versionName "${versionMajor}.${versionMinor}.${versionPatch}${getFdroidVersionSuffix()}" -// buildConfigField "String", "SHORT_FLAVOR_DESCRIPTION", "\"RC\"" -// buildConfigField "String", "FLAVOR_DESCRIPTION", "\"RustCrypto\"" -// } -// } -// publishNonDefault true } diff --git a/matrix-sdk-android/src/androidTest/assets/crypto_store_migration_16.realm b/matrix-sdk-android/src/androidTest/assets/crypto_store_migration_16.realm new file mode 100644 index 0000000000..4995bfc4a1 --- /dev/null +++ b/matrix-sdk-android/src/androidTest/assets/crypto_store_migration_16.realm @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:59b4957aa2f9cdc17b14ec8546e144537fac9dee050c6eb173f56fa8602c2736 +size 2097152 diff --git a/matrix-sdk-android/src/androidTest/assets/session_42.realm b/matrix-sdk-android/src/androidTest/assets/session_42.realm index b92d13dab2..9268111699 100644 Binary files a/matrix-sdk-android/src/androidTest/assets/session_42.realm and b/matrix-sdk-android/src/androidTest/assets/session_42.realm differ diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/ExtensionsKtTest.kt b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/ExtensionsKtTest.kt index 936dc6a872..cf74934700 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/ExtensionsKtTest.kt +++ b/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/crosssigning/ExtensionsKtTest.kt @@ -20,6 +20,7 @@ import org.amshove.kluent.shouldBeNull import org.amshove.kluent.shouldBeTrue import org.junit.Test import org.matrix.android.sdk.api.util.fromBase64 +import org.matrix.android.sdk.api.util.fromBase64Safe @Suppress("SpellCheckingInspection") class ExtensionsKtTest { diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt b/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt similarity index 100% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt rename to matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/PreShareKeysTest.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt b/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt similarity index 100% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt rename to matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/UnwedgingTest.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt similarity index 100% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt rename to matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/keysbackup/KeysBackupTest.kt diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt b/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt similarity index 99% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt rename to matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt index bf65b7857f..1b51886735 100644 --- a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt +++ b/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/SASTest.kt @@ -54,6 +54,8 @@ import java.util.concurrent.CountDownLatch @Ignore class SASTest : InstrumentedTest { + /* + @Test fun test_aliceStartThenAliceCancel() = runCryptoTest(context()) { cryptoTestHelper, testHelper -> // TODO @@ -614,4 +616,6 @@ class SASTest : InstrumentedTest { bobPovTx?.state() == SasTransactionState.SasShortCodeReady } } + + */ } diff --git a/matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/QrCodeTest.kt similarity index 100% rename from matrix-sdk-android/src/androidTest/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/QrCodeTest.kt rename to matrix-sdk-android/src/androidTestKotlinCrypto/java/org/matrix/android/sdk/internal/crypto/verification/qrcode/QrCodeTest.kt diff --git a/matrix-sdk-android/src/androidTestRustCrypto/java/org/matrix/android/sdk/internal/crypto/store/migration/ElementAndroidToElementRMigrationTest.kt b/matrix-sdk-android/src/androidTestRustCrypto/java/org/matrix/android/sdk/internal/crypto/store/migration/ElementAndroidToElementRMigrationTest.kt new file mode 100644 index 0000000000..0c5fb8c975 --- /dev/null +++ b/matrix-sdk-android/src/androidTestRustCrypto/java/org/matrix/android/sdk/internal/crypto/store/migration/ElementAndroidToElementRMigrationTest.kt @@ -0,0 +1,127 @@ +/* + * 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.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.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.matrix.android.sdk.InstrumentedTest +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.model.CryptoMetadataEntity +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.database.TestRealmConfigurationFactory +import org.matrix.android.sdk.internal.session.MigrateEAtoEROperation +import org.matrix.android.sdk.internal.util.time.Clock +import org.matrix.olm.OlmAccount +import org.matrix.olm.OlmManager +import org.matrix.rustcomponents.sdk.crypto.OlmMachine +import java.io.File + +@RunWith(AndroidJUnit4::class) +class ElementAndroidToElementRMigrationTest : InstrumentedTest { + + @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() + } + + @Test + fun given_a_valid_crypto_store_realm_file_then_migration_should_be_successful() { + val realmName = "crypto_store_migration_16.realm" + val migration = RealmCryptoStoreMigration(object : Clock { + override fun epochMillis() = 0L + }) + + val realmConfiguration = configurationFactory.createConfiguration( + realmName, + null, + RealmCryptoStoreModule(), + migration.schemaVersion, + migration + ) + configurationFactory.copyRealmFromAssets(context, realmName, realmName) + + realm = Realm.getInstance(realmConfiguration) + + val metaData = realm!!.where().findFirst()!! + val userId = metaData.userId!! + val deviceId = metaData.deviceId!! + val olmAccount = metaData.getOlmAccount()!! + + val extractor = MigrateEAtoEROperation() + + val targetFile = File(configurationFactory.root, "rust-sdk") + + extractor.execute(realmConfiguration, targetFile) + + val machine = OlmMachine(userId, deviceId, targetFile.path, null) + + 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) + + val inboundGroupSessionEntities = realm!!.where().findAll() + assertEquals(inboundGroupSessionEntities.size, machine.roomKeyCounts().total.toInt()) + + val backedUpInboundGroupSessionEntities = realm!! + .where() + .equalTo(OlmInboundGroupSessionEntityFields.BACKED_UP, true) + .findAll() + assertEquals(backedUpInboundGroupSessionEntities.size, machine.roomKeyCounts().backedUp.toInt()) + } + +// @Test +// fun given_an_empty_crypto_store_realm_file_then_migration_should_not_happen() { +// val realmConfiguration = realmConfigurationFactory.configurationForMigrationFrom15To16(populateCryptoStore = false) +// Realm.getInstance(realmConfiguration).use { +// assertTrue(it.isEmpty) +// } +// val machine = OlmMachine("@ganfra146:matrix.org", "UTDQCHKKNS", realmConfigurationFactory.root.path, null) +// assertNull(machine.getBackupKeys()) +// val crossSigningStatus = machine.crossSigningStatus() +// assertFalse(crossSigningStatus.hasMaster) +// assertFalse(crossSigningStatus.hasSelfSigning) +// assertFalse(crossSigningStatus.hasUserSigning) +// } +} diff --git a/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt b/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt index 16098e5210..b8f1746664 100644 --- a/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt +++ b/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/crypto/crosssigning/Extensions.kt @@ -15,11 +15,9 @@ */ package org.matrix.android.sdk.internal.crypto.crosssigning -import android.util.Base64 import org.matrix.android.sdk.api.session.crypto.crosssigning.CryptoCrossSigningKey import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.util.JsonCanonicalizer -import timber.log.Timber internal fun CryptoDeviceInfo.canonicalSignable(): String { return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary()) @@ -28,15 +26,3 @@ internal fun CryptoDeviceInfo.canonicalSignable(): String { internal fun CryptoCrossSigningKey.canonicalSignable(): String { return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary()) } - -/** - * Decode the base 64. Return null in case of bad format. Should be used when parsing received data from external source - */ -internal fun String.fromBase64Safe(): ByteArray? { - return try { - Base64.decode(this, Base64.DEFAULT) - } catch (throwable: Throwable) { - Timber.e(throwable, "Unable to decode base64 string") - null - } -} diff --git a/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/session/MigrateEAtoEROperation.kt b/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/session/MigrateEAtoEROperation.kt new file mode 100644 index 0000000000..0b76b2518a --- /dev/null +++ b/matrix-sdk-android/src/kotlinCrypto/java/org/matrix/android/sdk/internal/session/MigrateEAtoEROperation.kt @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021 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.RealmConfiguration +import java.io.File + +class MigrateEAtoEROperation { + + fun execute(cryptoRealm: RealmConfiguration, sessionFilesDir: File): File { + // no op in kotlinCrypto + return sessionFilesDir + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/Base64.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/Base64.kt index e0596c1325..6ffc82fc9b 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/Base64.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/util/Base64.kt @@ -17,6 +17,7 @@ package org.matrix.android.sdk.api.util import android.util.Base64 +import timber.log.Timber fun ByteArray.toBase64NoPadding(): String { return Base64.encodeToString(this, Base64.NO_PADDING or Base64.NO_WRAP) @@ -25,3 +26,15 @@ fun ByteArray.toBase64NoPadding(): String { fun String.fromBase64(): ByteArray { return Base64.decode(this, Base64.DEFAULT) } + +/** + * Decode the base 64. Return null in case of bad format. Should be used when parsing received data from external source + */ +internal fun String.fromBase64Safe(): ByteArray? { + return try { + Base64.decode(this, Base64.DEFAULT) + } catch (throwable: Throwable) { + Timber.e(throwable, "Unable to decode base64 string") + null + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt index 31aa0b83ca..076bbb3a7e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStore.kt @@ -146,37 +146,38 @@ internal class RealmCryptoStore @Inject constructor( .setWriteAsyncExecutor(monarchyWriteAsyncExecutor) .build() - init { - // Ensure CryptoMetadataEntity is inserted in DB - doRealmTransaction(realmConfiguration) { realm -> - var currentMetadata = realm.where().findFirst() +// init { +// // Ensure CryptoMetadataEntity is inserted in DB +// doRealmTransaction(realmConfiguration) { realm -> +// var currentMetadata = realm.where().findFirst() +// +// var deleteAll = false +// +// if (currentMetadata != null) { +// // Check credentials +// // The device id may not have been provided in credentials. +// // Check it only if provided, else trust the stored one. +// if (currentMetadata.userId != userId || +// (deviceId != currentMetadata.deviceId)) { +// Timber.w("## open() : Credentials do not match, close this store and delete data") +// deleteAll = true +// currentMetadata = null +// } +// } +// +// if (currentMetadata == null) { +// if (deleteAll) { +// realm.deleteAll() +// } +// +// // Metadata not found, or database cleaned, create it +// realm.createObject(CryptoMetadataEntity::class.java, userId).apply { +// deviceId = this@RealmCryptoStore.deviceId +// } +// } +// } +// } - var deleteAll = false - - if (currentMetadata != null) { - // Check credentials - // The device id may not have been provided in credentials. - // Check it only if provided, else trust the stored one. - if (currentMetadata.userId != userId || - (deviceId != currentMetadata.deviceId)) { - Timber.w("## open() : Credentials do not match, close this store and delete data") - deleteAll = true - currentMetadata = null - } - } - - if (currentMetadata == null) { - if (deleteAll) { - realm.deleteAll() - } - - // Metadata not found, or database cleaned, create it - realm.createObject(CryptoMetadataEntity::class.java, userId).apply { - deviceId = this@RealmCryptoStore.deviceId - } - } - } - } /* ========================================================================================== * Other data * ========================================================================================== */ diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/FileQualifiers.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/FileQualifiers.kt index 74dbd647ab..6715a6c098 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/FileQualifiers.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/di/FileQualifiers.kt @@ -33,3 +33,7 @@ internal annotation class CacheDirectory @Qualifier @Retention(AnnotationRetention.RUNTIME) internal annotation class ExternalFilesDirectory + +@Qualifier +@Retention(AnnotationRetention.RUNTIME) +internal annotation class SessionRustFilesDirectory diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt index 7d52d9b2bf..3ce3d1df0c 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/legacy/DefaultLegacySessionImporter.kt @@ -34,6 +34,7 @@ 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.database.RealmKeysUtils import org.matrix.android.sdk.internal.legacy.riot.LoginStorage +import org.matrix.android.sdk.internal.util.time.Clock import timber.log.Timber import java.io.File import javax.inject.Inject @@ -44,7 +45,7 @@ internal class DefaultLegacySessionImporter @Inject constructor( private val context: Context, private val sessionParamsStore: SessionParamsStore, private val realmKeysUtils: RealmKeysUtils, - private val realmCryptoStoreMigration: RealmCryptoStoreMigration + private val clock: Clock, ) : LegacySessionImporter { private val loginStorage = LoginStorage(context) @@ -167,6 +168,8 @@ internal class DefaultLegacySessionImporter @Inject constructor( newLocation.deleteRecursively() newLocation.mkdirs() + val realmCryptoStoreMigration = RealmCryptoStoreMigration(clock) + Timber.d("Migration: create legacy realm configuration") val realmConfiguration = RealmConfiguration.Builder() diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt index cc78670f7d..27647ba86a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/SessionModule.kt @@ -52,11 +52,13 @@ import org.matrix.android.sdk.internal.database.RealmSessionProvider import org.matrix.android.sdk.internal.database.SessionRealmConfigurationFactory import org.matrix.android.sdk.internal.di.Authenticated import org.matrix.android.sdk.internal.di.CacheDirectory +import org.matrix.android.sdk.internal.di.CryptoDatabase import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.SessionDatabase import org.matrix.android.sdk.internal.di.SessionDownloadsDirectory import org.matrix.android.sdk.internal.di.SessionFilesDirectory import org.matrix.android.sdk.internal.di.SessionId +import org.matrix.android.sdk.internal.di.SessionRustFilesDirectory import org.matrix.android.sdk.internal.di.Unauthenticated import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificate import org.matrix.android.sdk.internal.di.UnauthenticatedWithCertificateWithProgress @@ -178,6 +180,18 @@ internal abstract class SessionModule { return File(context.filesDir, sessionId) } + @JvmStatic + @Provides + @SessionRustFilesDirectory + @SessionScope + fun providesRustCryptoFilesDir( + @SessionFilesDirectory parent: File, + @CryptoDatabase realmConfiguration: RealmConfiguration, + ): File { + val target = File(parent, "rustFlavor") + return MigrateEAtoEROperation().execute(realmConfiguration, target) + } + @JvmStatic @Provides @SessionDownloadsDirectory diff --git a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt index dfa89bafb9..2e02a5aba2 100644 --- a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt +++ b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/OlmMachine.kt @@ -60,7 +60,7 @@ import org.matrix.android.sdk.internal.crypto.verification.VerificationsProvider import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeVerification import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.MoshiProvider -import org.matrix.android.sdk.internal.di.SessionFilesDirectory +import org.matrix.android.sdk.internal.di.SessionRustFilesDirectory import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.network.parsing.CheckNumberType import org.matrix.android.sdk.internal.session.SessionScope @@ -121,7 +121,7 @@ fun setRustLogger() { internal class OlmMachine @Inject constructor( @UserId userId: String, @DeviceId deviceId: String, - @SessionFilesDirectory path: File, + @SessionRustFilesDirectory path: File, private val requestSender: RequestSender, private val coroutineDispatchers: MatrixCoroutineDispatchers, baseMoshi: Moshi, diff --git a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/store/db/migration/rust/ExtractMigrationDataFailure.kt b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/store/db/migration/rust/ExtractMigrationDataFailure.kt new file mode 100644 index 0000000000..8e86402916 --- /dev/null +++ b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/store/db/migration/rust/ExtractMigrationDataFailure.kt @@ -0,0 +1,19 @@ +/* + * 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 + +object ExtractMigrationDataFailure : java.lang.RuntimeException("Can't proceed with migration, crypto store is empty or some necessary data is missing.") diff --git a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/store/db/migration/rust/ExtractMigrationDataUseCase.kt b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/store/db/migration/rust/ExtractMigrationDataUseCase.kt new file mode 100644 index 0000000000..14097217db --- /dev/null +++ b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/crypto/store/db/migration/rust/ExtractMigrationDataUseCase.kt @@ -0,0 +1,149 @@ +/* + * 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.deserializeFromRealm +import org.matrix.android.sdk.internal.crypto.store.db.model.CryptoMetadataEntity +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.UserEntity +import org.matrix.olm.OlmSession +import org.matrix.olm.OlmUtility +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 + +private val charset = Charset.forName("UTF-8") + +internal class ExtractMigrationDataUseCase { + + fun extractData(realm: Realm): MigrationData { + return try { + extract(realm) ?: throw ExtractMigrationDataFailure + } catch (failure: Throwable) { + throw ExtractMigrationDataFailure + } + } + + fun hasExistingData(realmConfiguration: RealmConfiguration): Boolean { + return Realm.getInstance(realmConfiguration).use { realm -> + !realm.isEmpty && + // Check if there is a MetaData object + realm.where().count() > 0 + } + } + + private fun extract(realm: Realm): MigrationData? { + val metadataEntity = realm.where().findFirst() ?: return null.also { + Timber.w("Rust db migration: No existing metadataEntity") + } + + val pickleKey = OlmUtility.getRandomKey() + val olmSessionEntities = realm.where().findAll() + val pickledSessions = olmSessionEntities.map { it.toPickledSession(pickleKey) } + + val inboundGroupSessionEntities = realm.where().findAll() + val pickledInboundGroupSessions = inboundGroupSessionEntities.mapNotNull { it.toPickledInboundGroupSession(pickleKey) } + + val masterKey = metadataEntity.xSignMasterPrivateKey + val userKey = metadataEntity.xSignUserPrivateKey + val selfSignedKey = metadataEntity.xSignSelfSignedPrivateKey + + val userId = metadataEntity.userId ?: return null + val deviceId = metadataEntity.deviceId ?: return null + val backupVersion = metadataEntity.backupVersion + val backupRecoveryKey = metadataEntity.keyBackupRecoveryKey + + val trackedUserEntities = realm.where().findAll() + val trackedUserIds = trackedUserEntities.mapNotNull { + it.userId + } + val isOlmAccountShared = metadataEntity.deviceKeysSentToServer + + val olmAccount = metadataEntity.getOlmAccount() ?: return null + val pickledOlmAccount = olmAccount.pickle(pickleKey, StringBuffer()).asString() + val pickledAccount = PickledAccount( + userId = userId, + deviceId = deviceId, + pickle = pickledOlmAccount, + shared = isOlmAccountShared, + uploadedSignedKeyCount = 50 + ) + return MigrationData( + account = pickledAccount, + sessions = pickledSessions, + inboundGroupSessions = pickledInboundGroupSessions, + pickleKey = pickleKey.map { it.toUByte() }, + backupVersion = backupVersion, + backupRecoveryKey = backupRecoveryKey, + crossSigning = CrossSigningKeyExport( + masterKey = masterKey, + selfSigningKey = selfSignedKey, + userSigningKey = userKey + ), + trackedUsers = trackedUserIds + ) + } + + 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 = true, + backedUp = backedUp + ) + } + + private fun OlmSessionEntity.toPickledSession(pickleKey: ByteArray): PickledSession { + val deviceKey = this.deviceKey ?: "" + val lastReceivedMessageTs = this.lastReceivedMessageTs + val olmSessionStr = this.olmSessionData + val olmSession = deserializeFromRealm(olmSessionStr)!! + val pickledOlmSession = olmSession.pickle(pickleKey, StringBuffer()).asString() + return PickledSession( + pickle = pickledOlmSession, + senderKey = deviceKey, + createdUsingFallbackKey = false, + creationTime = lastReceivedMessageTs.toString(), + lastUseTime = lastReceivedMessageTs.toString() + ) + } + + private fun ByteArray.asString() = String(this, charset) +} diff --git a/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/session/MigrateEAtoEROperation.kt b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/session/MigrateEAtoEROperation.kt new file mode 100644 index 0000000000..67ee7f321b --- /dev/null +++ b/matrix-sdk-android/src/rustCrypto/java/org/matrix/android/sdk/internal/session/MigrateEAtoEROperation.kt @@ -0,0 +1,52 @@ +/* + * 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.Realm +import io.realm.RealmConfiguration +import org.matrix.android.sdk.internal.crypto.store.db.migration.rust.ExtractMigrationDataUseCase +import org.matrix.rustcomponents.sdk.crypto.ProgressListener +import timber.log.Timber +import java.io.File + +class MigrateEAtoEROperation { + + fun execute(cryptoRealm: RealmConfiguration, sessionFilesDir: File): File { + // Temporary code for migration + if (!sessionFilesDir.exists()) { + sessionFilesDir.mkdir() + // perform a migration? + val extractMigrationData = ExtractMigrationDataUseCase() + try { + val progressListener = object : ProgressListener { + override fun onProgress(progress: Int, total: Int) { + Timber.v("OnProgress: $progress/$total") + } + } + + Realm.getInstance(cryptoRealm).use { realm -> + val migrationData = extractMigrationData.extractData(realm) + org.matrix.rustcomponents.sdk.crypto.migrate(migrationData, sessionFilesDir.path, null, progressListener) + } + } catch (failure: Throwable) { + Timber.e(failure, "Failure while calling rust migration method") + throw failure + } + } + return sessionFilesDir + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/list/home/RoomSummaryRoomListDiffCallback.kt b/vector/src/main/java/im/vector/app/features/home/room/list/home/RoomSummaryRoomListDiffCallback.kt index b24118b81b..0d61e930d3 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/list/home/RoomSummaryRoomListDiffCallback.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/list/home/RoomSummaryRoomListDiffCallback.kt @@ -23,7 +23,7 @@ import javax.inject.Inject class RoomSummaryRoomListDiffCallback @Inject constructor( vectorPreferences: VectorPreferences -): DiffUtil.ItemCallback() { +) : DiffUtil.ItemCallback() { override fun areItemsTheSame(oldItem: RoomSummary, newItem: RoomSummary): Boolean { return oldItem.roomId == newItem.roomId