diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt index 99fb41b10d..febf26baf0 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/crypto/CryptoService.kt @@ -40,6 +40,7 @@ import org.matrix.android.sdk.api.session.crypto.verification.VerificationServic import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent +import org.matrix.android.sdk.internal.database.helper.SessionInfoPair interface CryptoService { @@ -178,7 +179,12 @@ interface CryptoService { fun prepareToEncrypt(roomId: String, callback: MatrixCallback) /** - * Share existing inbound sessions with the provided userId devices + * Share all existing inbound sessions to the provided userId devices */ fun sendSharedHistoryKeys(roomId: String, userId: String) + + /** + * Share all inbound sessions of the last chunk messages to the provided userId devices + */ + fun sendSharedHistoryKeysToLastChunk(roomId: String, userId: String, sessionInfoSet: Set?) } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt index 4a44e9e2d0..46457ee494 100755 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/DefaultCryptoService.kt @@ -92,6 +92,7 @@ import org.matrix.android.sdk.internal.crypto.tasks.SetDeviceNameTask import org.matrix.android.sdk.internal.crypto.tasks.UploadKeysTask import org.matrix.android.sdk.internal.crypto.verification.DefaultVerificationService import org.matrix.android.sdk.internal.crypto.verification.VerificationMessageProcessor +import org.matrix.android.sdk.internal.database.helper.SessionInfoPair import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.MoshiProvider import org.matrix.android.sdk.internal.di.UserId @@ -1337,6 +1338,34 @@ internal class DefaultCryptoService @Inject constructor( } } + override fun sendSharedHistoryKeysToLastChunk(roomId: String, userId: String, sessionInfoSet: Set?) { + cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { + runCatching { + deviceListManager.downloadKeys(listOf(userId), false) + }.mapCatching { + val userDevices = cryptoStore.getUserDevices(userId) + userDevices?.forEach { + // Lets share the provided inbound sessions for every user device + val deviceId = it.key + sessionInfoSet?.mapNotNull { sessionInfoPair -> + // Get inbound session from sessionId and sessionKey + cryptoStore.getInboundGroupSession(sessionInfoPair.first, sessionInfoPair.second) + }?.filter { inboundGroupSession -> + // Filter only sessions with sharedHistory enabled + inboundGroupSession.sharedHistory + }?.forEach { inboundGroupSession -> + // Share the session to userId with deviceId + val exportedKeys = inboundGroupSession.exportKeys() + val algorithm = exportedKeys?.algorithm + val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) + decryptor?.shareKeysWithDevice(exportedKeys, deviceId, userId) + Timber.i("## CRYPTO | Sharing inbound session") + } + } + } + } + } + override fun sendSharedHistoryKeys(roomId: String, userId: String) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { val userDevices = cryptoStore.getUserDevices(userId) @@ -1347,7 +1376,7 @@ internal class DefaultCryptoService @Inject constructor( inboundSessions.filter { inboundGroupSession -> inboundGroupSession.sharedHistory }.forEach { inboundGroupSession -> - // Share the session with the to userId with deviceId + // Share the session to userId with deviceId val exportedKeys = inboundGroupSession.exportKeys() val algorithm = exportedKeys?.algorithm val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, algorithm) diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt index 234caec970..73af812271 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/helper/ChunkEntityHelper.kt @@ -18,7 +18,10 @@ package org.matrix.android.sdk.internal.database.helper import io.realm.Realm import io.realm.kotlin.createObject +import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent +import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.room.model.RoomMemberContent +import org.matrix.android.sdk.internal.database.mapper.asDomain import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.CurrentStateEventEntityFields import org.matrix.android.sdk.internal.database.model.EventAnnotationsSummaryEntity @@ -31,6 +34,7 @@ import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFie import org.matrix.android.sdk.internal.database.model.TimelineEventEntity import org.matrix.android.sdk.internal.database.model.TimelineEventEntityFields import org.matrix.android.sdk.internal.database.query.find +import org.matrix.android.sdk.internal.database.query.findLastForwardChunkOfRoom import org.matrix.android.sdk.internal.database.query.getOrCreate import org.matrix.android.sdk.internal.database.query.where import org.matrix.android.sdk.internal.session.room.timeline.PaginationDirection @@ -180,3 +184,14 @@ internal fun ChunkEntity.isMoreRecentThan(chunkToCheck: ChunkEntity): Boolean { // We don't know, so we assume it's false return false } + +internal fun ChunkEntity.Companion.findLatestSessionInfo(realm: Realm, roomId: String): Set? = + ChunkEntity.findLastForwardChunkOfRoom(realm, roomId)?.timelineEvents?.mapNotNull { timelineEvent -> + timelineEvent?.root?.asDomain()?.content?.toModel()?.let { content -> + content.sessionId ?: return@mapNotNull null + content.senderKey ?: return@mapNotNull null + Pair(content.sessionId, content.senderKey) + } + }?.toSet() + +internal typealias SessionInfoPair = Pair diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt index 2b3fa10c89..b6dbaa7be8 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/membership/DefaultMembershipService.kt @@ -29,7 +29,9 @@ import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams import org.matrix.android.sdk.api.session.room.model.Membership import org.matrix.android.sdk.api.session.room.model.RoomMemberSummary +import org.matrix.android.sdk.internal.database.helper.findLatestSessionInfo import org.matrix.android.sdk.internal.database.mapper.asDomain +import org.matrix.android.sdk.internal.database.model.ChunkEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntity import org.matrix.android.sdk.internal.database.model.RoomMemberSummaryEntityFields import org.matrix.android.sdk.internal.database.model.RoomMembersLoadStatusType @@ -141,7 +143,10 @@ internal class DefaultMembershipService @AssistedInject constructor( } override suspend fun invite(userId: String, reason: String?) { - cryptoService.sendSharedHistoryKeys(roomId, userId) + val sessionInfoSet = Realm.getInstance(monarchy.realmConfiguration).use { + ChunkEntity.findLatestSessionInfo(it, roomId) + } + cryptoService.sendSharedHistoryKeysToLastChunk(roomId, userId, sessionInfoSet) val params = InviteTask.Params(roomId, userId, reason) inviteTask.execute(params) }