diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt index 6847a46369..ce445d3ce6 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/IMXDecrypting.kt @@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.crypto.algorithms import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.model.MXEventDecryptionResult import org.matrix.android.sdk.api.session.events.model.Event +import org.matrix.android.sdk.internal.crypto.MegolmSessionData import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService /** @@ -43,4 +44,6 @@ internal interface IMXDecrypting { * @param defaultKeysBackupService the keys backup service */ fun onRoomKeyEvent(event: Event, defaultKeysBackupService: DefaultKeysBackupService) {} + + fun shareKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) {} } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt index 141d6f74cd..a9ffe5f008 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/crypto/algorithms/megolm/MXMegolmDecryption.kt @@ -28,7 +28,9 @@ import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventCon import org.matrix.android.sdk.api.session.events.model.content.RoomKeyContent import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.internal.crypto.MXOlmDevice -import org.matrix.android.sdk.internal.crypto.OutgoingKeyRequestManager +import org.matrix.android.sdk.internal.crypto.MegolmSessionData +import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction +import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter import org.matrix.android.sdk.internal.crypto.algorithms.IMXDecrypting import org.matrix.android.sdk.internal.crypto.keysbackup.DefaultKeysBackupService import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore @@ -307,4 +309,44 @@ internal class MXMegolmDecryption( Timber.tag(loggerTag.value).v("ON NEW SESSION $sessionId - $senderKey") newSessionListener?.onNewSession(roomId, senderKey, sessionId) } + override fun shareKeysWithDevice(exportedKeys: MegolmSessionData?, deviceId: String, userId: String) { + exportedKeys ?: return + cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { + runCatching { deviceListManager.downloadKeys(listOf(userId), false) } + .mapCatching { + val deviceInfo = cryptoStore.getUserDevice(userId, deviceId) + if (deviceInfo == null) { + throw RuntimeException() + } else { + val devicesByUser = mapOf(userId to listOf(deviceInfo)) + val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser) + val olmSessionResult = usersDeviceMap.getObject(userId, deviceId) + if (olmSessionResult?.sessionId == null) { + // no session with this device, probably because there + // were no one-time keys. + Timber.tag(loggerTag.value).e("no session with this device $deviceId, probably because there were no one-time keys.") + return@mapCatching + } + Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sharing session ${exportedKeys.sessionId} with device $userId:$deviceId") + + val payloadJson = mapOf( + "type" to EventType.FORWARDED_ROOM_KEY, + "content" to exportedKeys + ) + + val encodedPayload = messageEncrypter.encryptMessage(payloadJson, listOf(deviceInfo)) + val sendToDeviceMap = MXUsersDevicesMap() + sendToDeviceMap.setObject(userId, deviceId, encodedPayload) + Timber.tag(loggerTag.value).i("shareKeysWithDevice() : sending ${exportedKeys.sessionId} to $userId:$deviceId") + val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap) + try { + sendToDeviceTask.execute(sendToDeviceParams) + } catch (failure: Throwable) { + Timber.tag(loggerTag.value).e(failure, "shareKeysWithDevice() : Failed to send ${exportedKeys.sessionId} to $userId:$deviceId") + } + } + } + } + } + } 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 b5b8d8e974..b961e83b9a 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 @@ -66,6 +66,14 @@ internal interface IMXCryptoStore { */ fun getInboundGroupSessions(): List + /** + * Retrieve the known inbound group sessions for the specified room + * + * @param roomId The roomId that the sessions will be returned + * @return the list of all known group sessions, for the provided roomId + */ + fun getInboundGroupSessions(roomId: String): List + /** * @return true to unilaterally blacklist all unverified devices. */ 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 028d8f73f9..a3c7d6ccc1 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 @@ -828,6 +828,18 @@ internal class RealmCryptoStore @Inject constructor( } } + override fun getInboundGroupSessions(roomId: String): List { + return doWithRealm(realmConfiguration) { + it.where() + .findAll() + .mapNotNull { inboundGroupSessionEntity -> + inboundGroupSessionEntity.getInboundGroupSession() + }.filter { inboundSession -> + inboundSession.roomId == roomId + } + } + } + override fun removeInboundGroupSession(sessionId: String, senderKey: String) { val key = OlmInboundGroupSessionEntity.createPrimaryKey(sessionId, senderKey) 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 ec140e7b17..2b3fa10c89 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 @@ -23,6 +23,7 @@ import dagger.assisted.AssistedFactory import dagger.assisted.AssistedInject import io.realm.Realm import io.realm.RealmQuery +import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.identity.ThreePid import org.matrix.android.sdk.api.session.room.members.MembershipService import org.matrix.android.sdk.api.session.room.members.RoomMemberQueryParams @@ -50,6 +51,7 @@ internal class DefaultMembershipService @AssistedInject constructor( private val inviteThreePidTask: InviteThreePidTask, private val membershipAdminTask: MembershipAdminTask, private val roomDataSource: RoomDataSource, + private val cryptoService: CryptoService, @UserId private val userId: String, private val queryStringValueProcessor: QueryStringValueProcessor @@ -139,6 +141,7 @@ internal class DefaultMembershipService @AssistedInject constructor( } override suspend fun invite(userId: String, reason: String?) { + cryptoService.sendSharedHistoryKeys(roomId, userId) val params = InviteTask.Params(roomId, userId, reason) inviteTask.execute(params) }