diff --git a/CHANGES.md b/CHANGES.md index 6bf63456a908e265aab9401f61c5d10e78914e86..fde0a6d9d33dca374b2ed4ca84c544d3e9826eec 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,10 @@ Please also refer to the Changelog of Element Android: https://github.com/vector-im/element-android/blob/main/CHANGES.md +Changes in Matrix-SDK 1.2.2 (2021-09-13) +=================================================== + +Fix a security issue with message key sharing. See https://matrix.org/blog/2021/09/13/vulnerability-disclosure-key-sharing for details. + Changes in Matrix-SDK 1.2.1 (2021-09-08) =================================================== diff --git a/README.md b/README.md index 3b27a8b83df6f1bab22675a486315164a5e8ef3c..5c08dc614d47bc735598779b31101793411b9210 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[](https://jitpack.io/#matrix-org/matrix-android-sdk2) [](https://buildkite.com/matrix-dot-org/matrix-android-sdk2) + [](https://buildkite.com/matrix-dot-org/matrix-android-sdk2) # matrix-android-sdk2 @@ -23,12 +23,10 @@ Please open any issue in the Element Android project: [Create an issue](https:// To integrate the SDK to your application, add the following gradle dependency to the build.gradle of your application module: ```gradle -implementation 'com.github.matrix-org:matrix-android-sdk2:v0.0.1' +implementation 'org.matrix.android:matrix-android-sdk2:1.2.1' ``` -Latest version: [](https://jitpack.io/#matrix-org/matrix-android-sdk2) - -You need to add Jitpack as a repository in your main build.gradle file. Please follow instructions here: https://jitpack.io/#matrix-org/matrix-android-sdk2 +Latest version:  ### Sample application diff --git a/build.gradle b/build.gradle index c8a95ea2eca6b0a5d1dab09b98e7defe539bc64c..01c59b73575f3575b1b4f670405b2ba8a73078e9 100644 --- a/build.gradle +++ b/build.gradle @@ -10,11 +10,12 @@ buildscript { maven { url "https://plugins.gradle.org/m2/" } + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:7.0.2' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - + classpath 'com.vanniktech:gradle-maven-publish-plugin:0.17.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } @@ -42,6 +43,13 @@ allprojects { kotlinOptions.allWarningsAsErrors = false } + plugins.withId("com.vanniktech.maven.publish") { + // Publish on s01.oss.sonatype.org + //https://github.com/vanniktech/gradle-maven-publish-plugin#where-to-upload-to + mavenPublish { + sonatypeHost = "S01" + } + } } task clean(type: Delete) { diff --git a/gradle.properties b/gradle.properties index 99fd9d64fd27db2fd478ade850bd45130f1b43cb..16034b7377454ffdf66088b24000fcf2d664a849 100644 --- a/gradle.properties +++ b/gradle.properties @@ -21,3 +21,29 @@ vector.httpLogLevel=NONE # Note: to debug, you can put and uncomment the following lines in the file ~/.gradle/gradle.properties to override the value above #vector.debugPrivateData=true #vector.httpLogLevel=BODY + +# Maven publication +# Ref: https://github.com/vanniktech/gradle-maven-publish-plugin +GROUP=org.matrix.android +POM_ARTIFACT_ID=matrix-android-sdk2 +VERSION_NAME=1.2.2 + +POM_PACKAGING=aar + +POM_NAME=Matrix Android SDK 2 +POM_DESCRIPTION=An Android SDK to connect to a Matrix homeserver. +POM_INCEPTION_YEAR=2021 +POM_URL=https://github.com/matrix-org/matrix-android-sdk2 + +POM_LICENSE_NAME=The Apache Software License, Version 2.0 +POM_LICENCE_URL=https://www.apache.org/licenses/LICENSE-2.0.txt +POM_LICENCE_DIST=repo + +POM_SCM_URL=https://github.com/matrix-org/matrix-android-sdk2 +POM_SCM_CONNECTION=scm:git:git://github.com/matrix-org/matrix-android-sdk2.git +POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com/matrix-org/matrix-android-sdk2.git + +POM_DEVELOPER_ID=matrixdev +POM_DEVELOPER_NAME=matrixdev +POM_DEVELOPER_URL=https://github.com/matrix-org/ +POM_DEVELOPER_EMAIL=android@element.io diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 81136f43ae83ac5f08fc9199f2eaba8d1d123ae7..e1e1383d02d028d5a5a08989a0a503121d9d17a8 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-parcelize' apply plugin: 'realm-android' -apply plugin: 'maven-publish' +apply plugin: "com.vanniktech.maven.publish" buildscript { repositories { @@ -22,7 +22,7 @@ android { minSdkVersion 21 targetSdkVersion 30 versionCode 1 - versionName "1.2.1" + versionName "1.2.2" // Multidex is useful for tests multiDexEnabled true testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" @@ -193,13 +193,3 @@ dependencies { androidTestUtil 'androidx.test:orchestrator:1.4.0' } - -project.afterEvaluate { - publishing { - publications { - release(MavenPublication) { - from components.release - } - } - } -} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt index a7564444759960a157922029b4b68fbe3b89c911..540280d8a22d55325c54ddec9132be22144880ea 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt @@ -170,7 +170,7 @@ internal class MXMegolmEncryption( val deviceIds = devicesInRoom.getUserDeviceIds(userId) for (deviceId in deviceIds!!) { val deviceInfo = devicesInRoom.getObject(userId, deviceId) - if (deviceInfo != null && !cryptoStore.getSharedSessionInfo(roomId, safeSession.sessionId, userId, deviceId).found) { + if (deviceInfo != null && !cryptoStore.getSharedSessionInfo(roomId, safeSession.sessionId, deviceInfo).found) { val devices = shareMap.getOrPut(userId) { ArrayList() } devices.add(deviceInfo) } @@ -270,8 +270,8 @@ internal class MXMegolmEncryption( // for dead devices on every message. val gossipingEventBuffer = arrayListOf<Event>() for ((userId, devicesToShareWith) in devicesByUser) { - for ((deviceId) in devicesToShareWith) { - session.sharedWithHelper.markedSessionAsShared(userId, deviceId, chainIndex) + for (deviceInfo in devicesToShareWith) { + session.sharedWithHelper.markedSessionAsShared(deviceInfo, chainIndex) gossipingEventBuffer.add( Event( type = EventType.ROOM_KEY, @@ -279,7 +279,7 @@ internal class MXMegolmEncryption( content = submap.apply { this["session_key"] = "" // we add a fake key for trail - this["_dest"] = "$userId|$deviceId" + this["_dest"] = "$userId|${deviceInfo.deviceId}" } )) } @@ -429,7 +429,7 @@ internal class MXMegolmEncryption( .also { Timber.w("## Crypto reshareKey: Device not found") } // Get the chain index of the key we previously sent this device - val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, userId, deviceId) + val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, deviceInfo) if (!wasSessionSharedWithUser.found) { // This session was never shared with this user // Send a room key with held diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/SharedWithHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/SharedWithHelper.kt index f17168a6d22adb708647202484d55c6f50d396aa..a64e5af0d331c81d922d4a7e129af103351a6b81 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/SharedWithHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/SharedWithHelper.kt @@ -16,6 +16,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms.megolm +import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -28,7 +29,13 @@ internal class SharedWithHelper( return cryptoStore.getSharedWithInfo(roomId, sessionId) } - fun markedSessionAsShared(userId: String, deviceId: String, chainIndex: Int) { - cryptoStore.markedSessionAsShared(roomId, sessionId, userId, deviceId, chainIndex) + fun markedSessionAsShared(deviceInfo: CryptoDeviceInfo, chainIndex: Int) { + cryptoStore.markedSessionAsShared( + roomId = roomId, + sessionId = sessionId, + userId = deviceInfo.userId, + deviceId = deviceInfo.deviceId, + deviceIdentityKey = deviceInfo.identityKey() ?: "", + chainIndex = chainIndex) } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt index 3d12e74fcdd061833de6a12d988ee5abab3e3432..238d06738ccabaab272c75f54cac3411d78ca2e5 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/IMXCryptoStore.kt @@ -450,7 +450,8 @@ internal interface IMXCryptoStore { fun addWithHeldMegolmSession(withHeldContent: RoomKeyWithHeldContent) fun getWithHeldMegolmSession(roomId: String, sessionId: String): RoomKeyWithHeldContent? - fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) + fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, + deviceIdentityKey: String, chainIndex: Int) /** * Query for information on this session sharing history. @@ -459,7 +460,7 @@ internal interface IMXCryptoStore { * in this case chainIndex is not nullindicates the ratchet position. * In found is false, chainIndex is null */ - fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): SharedSessionResult + fun getSharedSessionInfo(roomId: String?, sessionId: String, deviceInfo: CryptoDeviceInfo): SharedSessionResult data class SharedSessionResult(val found: Boolean, val chainIndex: Int?) fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int> 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 3c8353e83e39c048aa94a4c5922fcc10632a7d51..860bba7404e305bd6f4790cf8dc9cb4d29777bbe 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 @@ -1681,7 +1681,12 @@ internal class RealmCryptoStore @Inject constructor( } } - override fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) { + override fun markedSessionAsShared(roomId: String?, + sessionId: String, + userId: String, + deviceId: String, + deviceIdentityKey: String, + chainIndex: Int) { doRealmTransaction(realmConfiguration) { realm -> SharedSessionEntity.create( realm = realm, @@ -1689,14 +1694,22 @@ internal class RealmCryptoStore @Inject constructor( sessionId = sessionId, userId = userId, deviceId = deviceId, + deviceIdentityKey = deviceIdentityKey, chainIndex = chainIndex ) } } - override fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): IMXCryptoStore.SharedSessionResult { + override fun getSharedSessionInfo(roomId: String?, sessionId: String, deviceInfo: CryptoDeviceInfo): IMXCryptoStore.SharedSessionResult { return doWithRealm(realmConfiguration) { realm -> - SharedSessionEntity.get(realm, roomId, sessionId, userId, deviceId)?.let { + SharedSessionEntity.get( + realm = realm, + roomId = roomId, + sessionId = sessionId, + userId = deviceInfo.userId, + deviceId = deviceInfo.deviceId, + deviceIdentityKey = deviceInfo.identityKey() + )?.let { IMXCryptoStore.SharedSessionResult(true, it.chainIndex) } ?: IMXCryptoStore.SharedSessionResult(false, null) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt index 2846be993255241c15d9f5279373581645acecad..f73cbaf480aa5e18e090ccd52115eb0b9a83fa1d 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/RealmCryptoStoreMigration.kt @@ -55,7 +55,7 @@ internal object RealmCryptoStoreMigration : RealmMigration { // 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) - const val CRYPTO_STORE_SCHEMA_VERSION = 13L + const val CRYPTO_STORE_SCHEMA_VERSION = 14L private fun RealmObjectSchema.addFieldIfNotExists(fieldName: String, fieldType: Class<*>): RealmObjectSchema { if (!hasField(fieldName)) { @@ -94,6 +94,7 @@ internal object RealmCryptoStoreMigration : RealmMigration { if (oldVersion <= 10) migrateTo11(realm) if (oldVersion <= 11) migrateTo12(realm) if (oldVersion <= 12) migrateTo13(realm) + if (oldVersion <= 13) migrateTo14(realm) } private fun migrateTo1Legacy(realm: DynamicRealm) { @@ -554,4 +555,21 @@ internal object RealmCryptoStoreMigration : RealmMigration { Timber.e("TrustLevelEntity cleanup: Something is not correct...") } } + + // Version 14L Update the way we remember key sharing + private fun migrateTo14(realm: DynamicRealm) { + Timber.d("Step 13 -> 14") + 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)) + } + } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/SharedSessionEntity.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/SharedSessionEntity.kt index c0ed1ac409a6caaa6798ffbcf378e41d91c3ac9b..e2ae512afd3baf8d098255b1ff854849d2daab97 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/SharedSessionEntity.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/model/SharedSessionEntity.kt @@ -30,6 +30,7 @@ internal open class SharedSessionEntity( @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() { diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/SharedSessionQueries.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/SharedSessionQueries.kt index fa37734fe581a163ad283f21ec7038ff24baf16a..39117512bb890694eace1e5c174fc857a26d9407 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/SharedSessionQueries.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/store/db/query/SharedSessionQueries.kt @@ -16,15 +16,20 @@ package org.matrix.android.sdk.internal.crypto.store.db.query -import org.matrix.android.sdk.internal.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 import io.realm.Realm import io.realm.RealmResults import io.realm.kotlin.createObject import io.realm.kotlin.where +import org.matrix.android.sdk.internal.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) +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) @@ -32,6 +37,7 @@ internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, se .equalTo(SharedSessionEntityFields.ALGORITHM, MXCRYPTO_ALGORITHM_MEGOLM) .equalTo(SharedSessionEntityFields.USER_ID, userId) .equalTo(SharedSessionEntityFields.DEVICE_ID, deviceId) + .equalTo(SharedSessionEntityFields.DEVICE_IDENTITY_KEY, deviceIdentityKey) .findFirst() } @@ -44,7 +50,12 @@ internal fun SharedSessionEntity.Companion.get(realm: Realm, roomId: String?, se .findAll() } -internal fun SharedSessionEntity.Companion.create(realm: Realm, roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) +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 @@ -52,6 +63,7 @@ internal fun SharedSessionEntity.Companion.create(realm: Realm, roomId: String?, this.sessionId = sessionId this.userId = userId this.deviceId = deviceId + this.deviceIdentityKey = deviceIdentityKey this.chainIndex = chainIndex } }