Persist outbound group session
This commit is contained in:
parent
b475f36b5a
commit
4ee34126bd
@ -6,6 +6,7 @@ Features ✨:
|
|||||||
|
|
||||||
Improvements 🙌:
|
Improvements 🙌:
|
||||||
- Add System theme option and set as default (#904) (#2387)
|
- Add System theme option and set as default (#904) (#2387)
|
||||||
|
- Store megolm outbound session to improve send time of first message after app launch.
|
||||||
|
|
||||||
Bugfix 🐛:
|
Bugfix 🐛:
|
||||||
- Unspecced msgType field in m.sticker (#2580)
|
- Unspecced msgType field in m.sticker (#2580)
|
||||||
|
@ -50,6 +50,8 @@ import org.junit.FixMethodOrder
|
|||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
import org.junit.runners.MethodSorters
|
import org.junit.runners.MethodSorters
|
||||||
|
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||||
|
import org.matrix.android.sdk.api.session.room.model.message.MessageContent
|
||||||
import java.util.concurrent.CountDownLatch
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
@ -296,4 +298,77 @@ class KeyShareTests : InstrumentedTest {
|
|||||||
mTestHelper.signOutAndClose(aliceSession1)
|
mTestHelper.signOutAndClose(aliceSession1)
|
||||||
mTestHelper.signOutAndClose(aliceSession2)
|
mTestHelper.signOutAndClose(aliceSession2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun test_ImproperKeyShareBug() {
|
||||||
|
val aliceSession = mTestHelper.createAccount(TestConstants.USER_ALICE, SessionTestParams(true))
|
||||||
|
|
||||||
|
mTestHelper.doSync<Unit> {
|
||||||
|
aliceSession.cryptoService().crossSigningService()
|
||||||
|
.initializeCrossSigning(UserPasswordAuth(
|
||||||
|
user = aliceSession.myUserId,
|
||||||
|
password = TestConstants.PASSWORD
|
||||||
|
), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an encrypted room and send a couple of messages
|
||||||
|
val roomId = mTestHelper.doSync<String> {
|
||||||
|
aliceSession.createRoom(
|
||||||
|
CreateRoomParams().apply {
|
||||||
|
visibility = RoomDirectoryVisibility.PRIVATE
|
||||||
|
enableEncryption()
|
||||||
|
},
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val roomAlicePov = aliceSession.getRoom(roomId)
|
||||||
|
assertNotNull(roomAlicePov)
|
||||||
|
Thread.sleep(1_000)
|
||||||
|
assertTrue(roomAlicePov?.isEncrypted() == true)
|
||||||
|
val secondEventId = mTestHelper.sendTextMessage(roomAlicePov!!, "Message", 3)[1].eventId
|
||||||
|
|
||||||
|
// Create bob session
|
||||||
|
|
||||||
|
val bobSession = mTestHelper.createAccount(TestConstants.USER_BOB, SessionTestParams(true))
|
||||||
|
mTestHelper.doSync<Unit> {
|
||||||
|
bobSession.cryptoService().crossSigningService()
|
||||||
|
.initializeCrossSigning(UserPasswordAuth(
|
||||||
|
user = bobSession.myUserId,
|
||||||
|
password = TestConstants.PASSWORD
|
||||||
|
), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let alice invite bob
|
||||||
|
mTestHelper.doSync<Unit> {
|
||||||
|
roomAlicePov.invite(bobSession.myUserId, null, it)
|
||||||
|
}
|
||||||
|
|
||||||
|
mTestHelper.doSync<Unit> {
|
||||||
|
bobSession.joinRoom(roomAlicePov.roomId, null, emptyList(), it)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we want to discard alice outbound session
|
||||||
|
aliceSession.cryptoService().discardOutboundSession(roomAlicePov.roomId)
|
||||||
|
|
||||||
|
// and now resend a new message to reset index to 0
|
||||||
|
mTestHelper.sendTextMessage(roomAlicePov, "After", 1)
|
||||||
|
|
||||||
|
val roomRoomBobPov = aliceSession.getRoom(roomId)
|
||||||
|
val beforeJoin = roomRoomBobPov!!.getTimeLineEvent(secondEventId)
|
||||||
|
|
||||||
|
var dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin!!.root, "") }
|
||||||
|
|
||||||
|
assert(dRes == null)
|
||||||
|
|
||||||
|
// Try to re-ask the keys
|
||||||
|
|
||||||
|
bobSession.cryptoService().reRequestRoomKeyForEvent(beforeJoin!!.root)
|
||||||
|
|
||||||
|
Thread.sleep(3_000)
|
||||||
|
|
||||||
|
// With the bug the first session would have improperly reshare that key :/
|
||||||
|
dRes = tryOrNull { bobSession.cryptoService().decryptEvent(beforeJoin.root, "") }
|
||||||
|
Log.d("#TEST", "KS: sgould not decrypt that ${beforeJoin.root.getClearContent().toModel<MessageContent>()?.body}")
|
||||||
|
assert(dRes?.clearEvent == null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,7 @@ import org.matrix.android.sdk.internal.crypto.crosssigning.toBase64NoPadding
|
|||||||
import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
|
import org.matrix.android.sdk.internal.crypto.keysbackup.util.extractCurveKeyFromRecoveryKey
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingDefaultContent
|
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingDefaultContent
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
|
import org.matrix.android.sdk.internal.crypto.model.rest.GossipingToDeviceObject
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
||||||
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
|
||||||
import org.matrix.android.sdk.internal.di.SessionId
|
import org.matrix.android.sdk.internal.di.SessionId
|
||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
@ -206,34 +207,7 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
|||||||
|
|
||||||
Timber.v("## CRYPTO | GOSSIP processIncomingRoomKeyRequest from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}")
|
Timber.v("## CRYPTO | GOSSIP processIncomingRoomKeyRequest from $userId:$deviceId for $roomId / ${body.sessionId} id ${request.requestId}")
|
||||||
if (credentials.userId != userId) {
|
if (credentials.userId != userId) {
|
||||||
Timber.w("## CRYPTO | GOSSIP processReceivedGossipingRequests() : room key request from other user")
|
handleKeyRequestFromOtherUser(body, request, alg, roomId, userId, deviceId)
|
||||||
val senderKey = body.senderKey ?: return Unit
|
|
||||||
.also { Timber.w("missing senderKey") }
|
|
||||||
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
|
|
||||||
val sessionId = body.sessionId ?: return Unit
|
|
||||||
.also { Timber.w("missing sessionId") }
|
|
||||||
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
|
|
||||||
|
|
||||||
if (alg != MXCRYPTO_ALGORITHM_MEGOLM) {
|
|
||||||
return Unit
|
|
||||||
.also { Timber.w("Only megolm is accepted here") }
|
|
||||||
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
|
|
||||||
}
|
|
||||||
|
|
||||||
val roomEncryptor = roomEncryptorsStore.get(roomId) ?: return Unit
|
|
||||||
.also { Timber.w("no room Encryptor") }
|
|
||||||
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
|
|
||||||
|
|
||||||
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
|
||||||
val isSuccess = roomEncryptor.reshareKey(sessionId, userId, deviceId, senderKey)
|
|
||||||
|
|
||||||
if (isSuccess) {
|
|
||||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
|
|
||||||
} else {
|
|
||||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.UNABLE_TO_PROCESS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.RE_REQUESTED)
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// TODO: should we queue up requests we don't yet have keys for, in case they turn up later?
|
// TODO: should we queue up requests we don't yet have keys for, in case they turn up later?
|
||||||
@ -291,6 +265,42 @@ internal class IncomingGossipingRequestManager @Inject constructor(
|
|||||||
onRoomKeyRequest(request)
|
onRoomKeyRequest(request)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleKeyRequestFromOtherUser(body: RoomKeyRequestBody,
|
||||||
|
request: IncomingRoomKeyRequest,
|
||||||
|
alg: String,
|
||||||
|
roomId: String,
|
||||||
|
userId: String,
|
||||||
|
deviceId: String) {
|
||||||
|
Timber.w("## CRYPTO | GOSSIP processReceivedGossipingRequests() : room key request from other user")
|
||||||
|
val senderKey = body.senderKey ?: return Unit
|
||||||
|
.also { Timber.w("missing senderKey") }
|
||||||
|
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
|
||||||
|
val sessionId = body.sessionId ?: return Unit
|
||||||
|
.also { Timber.w("missing sessionId") }
|
||||||
|
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
|
||||||
|
|
||||||
|
if (alg != MXCRYPTO_ALGORITHM_MEGOLM) {
|
||||||
|
return Unit
|
||||||
|
.also { Timber.w("Only megolm is accepted here") }
|
||||||
|
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
|
||||||
|
}
|
||||||
|
|
||||||
|
val roomEncryptor = roomEncryptorsStore.get(roomId) ?: return Unit
|
||||||
|
.also { Timber.w("no room Encryptor") }
|
||||||
|
.also { cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED) }
|
||||||
|
|
||||||
|
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
|
||||||
|
val isSuccess = roomEncryptor.reshareKey(sessionId, userId, deviceId, senderKey)
|
||||||
|
|
||||||
|
if (isSuccess) {
|
||||||
|
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.ACCEPTED)
|
||||||
|
} else {
|
||||||
|
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.UNABLE_TO_PROCESS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.RE_REQUESTED)
|
||||||
|
}
|
||||||
|
|
||||||
private fun processIncomingSecretShareRequest(request: IncomingSecretShareRequest) {
|
private fun processIncomingSecretShareRequest(request: IncomingSecretShareRequest) {
|
||||||
val secretName = request.secretName ?: return Unit.also {
|
val secretName = request.secretName ?: return Unit.also {
|
||||||
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
cryptoStore.updateGossipingRequestState(request, GossipingRequestState.REJECTED)
|
||||||
|
@ -19,6 +19,8 @@ package org.matrix.android.sdk.internal.crypto
|
|||||||
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
|
||||||
import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
import org.matrix.android.sdk.api.util.JSON_DICT_PARAMETERIZED_TYPE
|
||||||
import org.matrix.android.sdk.api.util.JsonDict
|
import org.matrix.android.sdk.api.util.JsonDict
|
||||||
|
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.MXOutboundSessionInfo
|
||||||
|
import org.matrix.android.sdk.internal.crypto.algorithms.megolm.SharedWithHelper
|
||||||
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
import org.matrix.android.sdk.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||||
@ -46,7 +48,7 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
private val store: IMXCryptoStore,
|
private val store: IMXCryptoStore,
|
||||||
private val inboundGroupSessionStore: InboundGroupSessionStore
|
private val inboundGroupSessionStore: InboundGroupSessionStore
|
||||||
) {
|
) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the Curve25519 key for the account.
|
* @return the Curve25519 key for the account.
|
||||||
@ -135,6 +137,10 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
fun release() {
|
fun release() {
|
||||||
olmUtility?.releaseUtility()
|
olmUtility?.releaseUtility()
|
||||||
|
outboundGroupSessionStore.values.forEach {
|
||||||
|
it.releaseSession()
|
||||||
|
}
|
||||||
|
outboundGroupSessionStore.clear()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -406,11 +412,12 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
*
|
*
|
||||||
* @return the session id for the outbound session.
|
* @return the session id for the outbound session.
|
||||||
*/
|
*/
|
||||||
fun createOutboundGroupSession(): String? {
|
fun createOutboundGroupSessionForRoom(roomId: String): String? {
|
||||||
var session: OlmOutboundGroupSession? = null
|
var session: OlmOutboundGroupSession? = null
|
||||||
try {
|
try {
|
||||||
session = OlmOutboundGroupSession()
|
session = OlmOutboundGroupSession()
|
||||||
outboundGroupSessionStore[session.sessionIdentifier()] = session
|
outboundGroupSessionStore[session.sessionIdentifier()] = session
|
||||||
|
store.storeCurrentOutboundGroupSessionForRoom(roomId, session)
|
||||||
return session.sessionIdentifier()
|
return session.sessionIdentifier()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Timber.e(e, "createOutboundGroupSession")
|
Timber.e(e, "createOutboundGroupSession")
|
||||||
@ -421,6 +428,34 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun storeOutboundGroupSessionForRoom(roomId: String, sessionId: String) {
|
||||||
|
outboundGroupSessionStore[sessionId]?.let {
|
||||||
|
store.storeCurrentOutboundGroupSessionForRoom(roomId, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun restoreOutboundGroupSessionForRoom(roomId: String): MXOutboundSessionInfo? {
|
||||||
|
val restoredOutboundGroupSession = store.getCurrentOutboundGroupSessionForRoom(roomId)
|
||||||
|
if (restoredOutboundGroupSession != null) {
|
||||||
|
val sessionId = restoredOutboundGroupSession.outboundGroupSession.sessionIdentifier()
|
||||||
|
if (!outboundGroupSessionStore.containsKey(sessionId)) {
|
||||||
|
outboundGroupSessionStore[sessionId] = restoredOutboundGroupSession.outboundGroupSession
|
||||||
|
return MXOutboundSessionInfo(
|
||||||
|
sessionId = sessionId,
|
||||||
|
sharedWithHelper = SharedWithHelper(roomId, sessionId, store),
|
||||||
|
restoredOutboundGroupSession.creationTime
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
restoredOutboundGroupSession.outboundGroupSession.releaseSession()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun discardOutboundGroupSessionForRoom(roomId: String) {
|
||||||
|
outboundGroupSessionStore.remove(roomId)?.releaseSession()
|
||||||
|
store.storeCurrentOutboundGroupSessionForRoom(roomId, null)
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Get the current session key of an outbound group session.
|
* Get the current session key of an outbound group session.
|
||||||
*
|
*
|
||||||
@ -747,7 +782,7 @@ internal class MXOlmDevice @Inject constructor(
|
|||||||
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)
|
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_SENDER_KEY, MXCryptoError.ERROR_MISSING_PROPERTY_REASON)
|
||||||
}
|
}
|
||||||
|
|
||||||
val session = inboundGroupSessionStore.getInboundGroupSession(sessionId, senderKey)
|
val session = inboundGroupSessionStore.getInboundGroupSession(sessionId, senderKey)
|
||||||
|
|
||||||
if (session != null) {
|
if (session != null) {
|
||||||
// Check that the room id matches the original one for the session. This stops
|
// Check that the room id matches the original one for the session. This stops
|
||||||
|
@ -68,6 +68,10 @@ internal class MXMegolmEncryption(
|
|||||||
// case outboundSession.shareOperation will be non-null.)
|
// case outboundSession.shareOperation will be non-null.)
|
||||||
private var outboundSession: MXOutboundSessionInfo? = null
|
private var outboundSession: MXOutboundSessionInfo? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
// restore existing outbound session if any
|
||||||
|
outboundSession = olmDevice.restoreOutboundGroupSessionForRoom(roomId)
|
||||||
|
}
|
||||||
// Default rotation periods
|
// Default rotation periods
|
||||||
// TODO: Make it configurable via parameters
|
// TODO: Make it configurable via parameters
|
||||||
// Session rotation periods
|
// Session rotation periods
|
||||||
@ -86,6 +90,9 @@ internal class MXMegolmEncryption(
|
|||||||
return encryptContent(outboundSession, eventType, eventContent)
|
return encryptContent(outboundSession, eventType, eventContent)
|
||||||
.also {
|
.also {
|
||||||
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
|
notifyWithheldForSession(devices.withHeldDevices, outboundSession)
|
||||||
|
// annoyingly we have to serialize again the saved outbound session to store message index :/
|
||||||
|
// if not we would see duplicate message index errors
|
||||||
|
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +114,7 @@ internal class MXMegolmEncryption(
|
|||||||
|
|
||||||
override fun discardSessionKey() {
|
override fun discardSessionKey() {
|
||||||
outboundSession = null
|
outboundSession = null
|
||||||
|
olmDevice.discardOutboundGroupSessionForRoom(roomId)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -116,7 +124,7 @@ internal class MXMegolmEncryption(
|
|||||||
*/
|
*/
|
||||||
private fun prepareNewSessionInRoom(): MXOutboundSessionInfo {
|
private fun prepareNewSessionInRoom(): MXOutboundSessionInfo {
|
||||||
Timber.v("## CRYPTO | prepareNewSessionInRoom() ")
|
Timber.v("## CRYPTO | prepareNewSessionInRoom() ")
|
||||||
val sessionId = olmDevice.createOutboundGroupSession()
|
val sessionId = olmDevice.createOutboundGroupSessionForRoom(roomId)
|
||||||
|
|
||||||
val keysClaimedMap = HashMap<String, String>()
|
val keysClaimedMap = HashMap<String, String>()
|
||||||
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!
|
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!
|
||||||
@ -152,7 +160,7 @@ internal class MXMegolmEncryption(
|
|||||||
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
|
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
|
||||||
for (deviceId in deviceIds!!) {
|
for (deviceId in deviceIds!!) {
|
||||||
val deviceInfo = devicesInRoom.getObject(userId, deviceId)
|
val deviceInfo = devicesInRoom.getObject(userId, deviceId)
|
||||||
if (deviceInfo != null && !cryptoStore.wasSessionSharedWithUser(roomId, safeSession.sessionId, userId, deviceId).found) {
|
if (deviceInfo != null && !cryptoStore.getSharedSessionInfo(roomId, safeSession.sessionId, userId, deviceId).found) {
|
||||||
val devices = shareMap.getOrPut(userId) { ArrayList() }
|
val devices = shareMap.getOrPut(userId) { ArrayList() }
|
||||||
devices.add(deviceInfo)
|
devices.add(deviceInfo)
|
||||||
}
|
}
|
||||||
@ -401,11 +409,18 @@ internal class MXMegolmEncryption(
|
|||||||
.also { Timber.w("## Crypto reshareKey: Device not found") }
|
.also { Timber.w("## Crypto reshareKey: Device not found") }
|
||||||
|
|
||||||
// Get the chain index of the key we previously sent this device
|
// Get the chain index of the key we previously sent this device
|
||||||
val chainIndex = outboundSession?.sharedWithHelper?.wasSharedWith(userId, deviceId) ?: return false
|
val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(roomId, sessionId, userId, deviceId)
|
||||||
|
if (!wasSessionSharedWithUser.found) {
|
||||||
|
// This session was never shared with this user
|
||||||
|
// Send a room key with held
|
||||||
|
notifyKeyWithHeld(listOf(UserDevice(userId, deviceId)), sessionId, senderKey, WithHeldCode.UNAUTHORISED)
|
||||||
|
Timber.w("## Crypto reshareKey: ERROR : Never shared megolm with this device")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// if found chain index should not be null
|
||||||
|
val chainIndex = wasSessionSharedWithUser.chainIndex ?: return false
|
||||||
.also {
|
.also {
|
||||||
// Send a room key with held
|
Timber.w("## Crypto reshareKey: Null chain index")
|
||||||
notifyKeyWithHeld(listOf(UserDevice(userId, deviceId)), sessionId, senderKey, WithHeldCode.UNAUTHORISED)
|
|
||||||
Timber.w("## Crypto reshareKey: ERROR : Never share megolm with this device")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
||||||
|
@ -23,9 +23,8 @@ import timber.log.Timber
|
|||||||
internal class MXOutboundSessionInfo(
|
internal class MXOutboundSessionInfo(
|
||||||
// The id of the session
|
// The id of the session
|
||||||
val sessionId: String,
|
val sessionId: String,
|
||||||
val sharedWithHelper: SharedWithHelper) {
|
val sharedWithHelper: SharedWithHelper,
|
||||||
// When the session was created
|
private val creationTime: Long = System.currentTimeMillis()) {
|
||||||
private val creationTime = System.currentTimeMillis()
|
|
||||||
|
|
||||||
// Number of times this session has been used
|
// Number of times this session has been used
|
||||||
var useCount: Int = 0
|
var useCount: Int = 0
|
||||||
|
@ -28,10 +28,6 @@ internal class SharedWithHelper(
|
|||||||
return cryptoStore.getSharedWithInfo(roomId, sessionId)
|
return cryptoStore.getSharedWithInfo(roomId, sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun wasSharedWith(userId: String, deviceId: String): Int? {
|
|
||||||
return cryptoStore.wasSessionSharedWithUser(roomId, sessionId, userId, deviceId).chainIndex
|
|
||||||
}
|
|
||||||
|
|
||||||
fun markedSessionAsShared(userId: String, deviceId: String, chainIndex: Int) {
|
fun markedSessionAsShared(userId: String, deviceId: String, chainIndex: Int) {
|
||||||
cryptoStore.markedSessionAsShared(roomId, sessionId, userId, deviceId, chainIndex)
|
cryptoStore.markedSessionAsShared(roomId, sessionId, userId, deviceId, chainIndex)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
data class OutboundGroupSessionWrapper(
|
||||||
|
val outboundGroupSession: OlmOutboundGroupSession,
|
||||||
|
val creationTime: Long
|
||||||
|
)
|
@ -33,11 +33,13 @@ 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.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper
|
||||||
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
|
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.KeysBackupDataEntity
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
|
import org.matrix.olm.OlmOutboundGroupSession
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* the crypto data store
|
* the crypto data store
|
||||||
@ -293,6 +295,16 @@ internal interface IMXCryptoStore {
|
|||||||
*/
|
*/
|
||||||
fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2?
|
fun getInboundGroupSession(sessionId: String, senderKey: String): OlmInboundGroupSessionWrapper2?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current outbound group session for this encrypted room
|
||||||
|
*/
|
||||||
|
fun getCurrentOutboundGroupSessionForRoom(roomId: String): OutboundGroupSessionWrapper?
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the current outbound group session for this encrypted room
|
||||||
|
*/
|
||||||
|
fun storeCurrentOutboundGroupSessionForRoom(roomId: String, outboundGroupSession: OlmOutboundGroupSession?)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove an inbound group session
|
* Remove an inbound group session
|
||||||
*
|
*
|
||||||
@ -439,7 +451,15 @@ internal interface IMXCryptoStore {
|
|||||||
fun getWithHeldMegolmSession(roomId: String, sessionId: String): 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, chainIndex: Int)
|
||||||
fun wasSessionSharedWithUser(roomId: String?, sessionId: String, userId: String, deviceId: String): SharedSessionResult
|
|
||||||
|
/**
|
||||||
|
* Query for information on this session sharing history.
|
||||||
|
* @return SharedSessionResult
|
||||||
|
* if found is true then this session was initialy shared with that user|device,
|
||||||
|
* 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
|
||||||
data class SharedSessionResult(val found: Boolean, val chainIndex: Int?)
|
data class SharedSessionResult(val found: Boolean, val chainIndex: Int?)
|
||||||
|
|
||||||
fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int>
|
fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int>
|
||||||
|
@ -47,6 +47,7 @@ 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.model.MXUsersDevicesMap
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
import org.matrix.android.sdk.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
||||||
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
import org.matrix.android.sdk.internal.crypto.model.OlmSessionWrapper
|
||||||
|
import org.matrix.android.sdk.internal.crypto.model.OutboundGroupSessionWrapper
|
||||||
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
|
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
|
||||||
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
import org.matrix.android.sdk.internal.crypto.model.rest.RoomKeyRequestBody
|
||||||
@ -73,6 +74,7 @@ import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSess
|
|||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
|
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.OlmSessionEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntityFields
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OlmSessionEntityFields
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.SharedSessionEntity
|
||||||
@ -95,6 +97,7 @@ import org.matrix.android.sdk.internal.di.UserId
|
|||||||
import org.matrix.android.sdk.internal.session.SessionScope
|
import org.matrix.android.sdk.internal.session.SessionScope
|
||||||
import org.matrix.olm.OlmAccount
|
import org.matrix.olm.OlmAccount
|
||||||
import org.matrix.olm.OlmException
|
import org.matrix.olm.OlmException
|
||||||
|
import org.matrix.olm.OlmOutboundGroupSession
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlin.collections.set
|
import kotlin.collections.set
|
||||||
@ -756,6 +759,44 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
return inboundGroupSessionToRelease[key]
|
return inboundGroupSessionToRelease[key]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getCurrentOutboundGroupSessionForRoom(roomId: String): OutboundGroupSessionWrapper? {
|
||||||
|
return doWithRealm(realmConfiguration) { realm ->
|
||||||
|
realm.where<CryptoRoomEntity>()
|
||||||
|
.equalTo(CryptoRoomEntityFields.ROOM_ID, roomId)
|
||||||
|
.findFirst()?.outboundSessionInfo?.let { entity ->
|
||||||
|
entity.getOutboundGroupSession()?.let {
|
||||||
|
OutboundGroupSessionWrapper(
|
||||||
|
it,
|
||||||
|
entity.creationTime ?: 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun storeCurrentOutboundGroupSessionForRoom(roomId: String, outboundGroupSession: OlmOutboundGroupSession?) {
|
||||||
|
// we can do this async, as it's just for restoring on next launch
|
||||||
|
// the olmdevice is caching the active instance
|
||||||
|
// this is called for each sent message (so not high frequency), thus we can use basic realm async without
|
||||||
|
// risk of reaching max async operation limit?
|
||||||
|
doRealmTransactionAsync(realmConfiguration) { realm ->
|
||||||
|
realm.where<CryptoRoomEntity>()
|
||||||
|
.equalTo(CryptoRoomEntityFields.ROOM_ID, roomId)
|
||||||
|
.findFirst()?.let { entity ->
|
||||||
|
// we should delete existing outbound session info if any
|
||||||
|
entity.outboundSessionInfo?.deleteFromRealm()
|
||||||
|
|
||||||
|
if (outboundGroupSession != null) {
|
||||||
|
val info = realm.createObject(OutboundGroupSessionInfoEntity::class.java).apply {
|
||||||
|
creationTime = System.currentTimeMillis()
|
||||||
|
putOutboundGroupSession(outboundGroupSession)
|
||||||
|
}
|
||||||
|
entity.outboundSessionInfo = info
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: the result will be only use to export all the keys and not to use the OlmInboundGroupSessionWrapper2,
|
* Note: the result will be only use to export all the keys and not to use the OlmInboundGroupSessionWrapper2,
|
||||||
* so there is no need to use or update `inboundGroupSessionToRelease` for native memory management
|
* so there is no need to use or update `inboundGroupSessionToRelease` for native memory management
|
||||||
@ -1645,7 +1686,7 @@ internal class RealmCryptoStore @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun wasSessionSharedWithUser(roomId: String?, sessionId: String, userId: String, deviceId: String): IMXCryptoStore.SharedSessionResult {
|
override fun getSharedSessionInfo(roomId: String?, sessionId: String, userId: String, deviceId: String): IMXCryptoStore.SharedSessionResult {
|
||||||
return doWithRealm(realmConfiguration) { realm ->
|
return doWithRealm(realmConfiguration) { realm ->
|
||||||
SharedSessionEntity.get(realm, roomId, sessionId, userId, deviceId)?.let {
|
SharedSessionEntity.get(realm, roomId, sessionId, userId, deviceId)?.let {
|
||||||
IMXCryptoStore.SharedSessionResult(true, it.chainIndex)
|
IMXCryptoStore.SharedSessionResult(true, it.chainIndex)
|
||||||
|
@ -44,6 +44,7 @@ import org.matrix.android.sdk.internal.di.SerializeNulls
|
|||||||
import io.realm.DynamicRealm
|
import io.realm.DynamicRealm
|
||||||
import io.realm.RealmMigration
|
import io.realm.RealmMigration
|
||||||
import io.realm.RealmObjectSchema
|
import io.realm.RealmObjectSchema
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntityFields
|
||||||
import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2
|
import org.matrix.androidsdk.crypto.data.MXOlmInboundGroupSession2
|
||||||
import timber.log.Timber
|
import timber.log.Timber
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -55,7 +56,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
|
|||||||
// 0, 1, 2: legacy Riot-Android
|
// 0, 1, 2: legacy Riot-Android
|
||||||
// 3: migrate to RiotX schema
|
// 3: migrate to RiotX schema
|
||||||
// 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6)
|
// 4, 5, 6, 7, 8, 9: migrations from RiotX (which was previously 1, 2, 3, 4, 5, 6)
|
||||||
const val CRYPTO_STORE_SCHEMA_VERSION = 11L
|
const val CRYPTO_STORE_SCHEMA_VERSION = 12L
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RealmObjectSchema.addFieldIfNotExists(fieldName: String, fieldType: Class<*>): RealmObjectSchema {
|
private fun RealmObjectSchema.addFieldIfNotExists(fieldName: String, fieldType: Class<*>): RealmObjectSchema {
|
||||||
@ -93,6 +94,7 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
|
|||||||
if (oldVersion <= 8) migrateTo9(realm)
|
if (oldVersion <= 8) migrateTo9(realm)
|
||||||
if (oldVersion <= 9) migrateTo10(realm)
|
if (oldVersion <= 9) migrateTo10(realm)
|
||||||
if (oldVersion <= 10) migrateTo11(realm)
|
if (oldVersion <= 10) migrateTo11(realm)
|
||||||
|
if (oldVersion <= 11) migrateTo12(realm)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun migrateTo1Legacy(realm: DynamicRealm) {
|
private fun migrateTo1Legacy(realm: DynamicRealm) {
|
||||||
@ -483,4 +485,16 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
|
|||||||
realm.schema.get("CryptoMetadataEntity")
|
realm.schema.get("CryptoMetadataEntity")
|
||||||
?.addField(CryptoMetadataEntityFields.DEVICE_KEYS_SENT_TO_SERVER, Boolean::class.java)
|
?.addField(CryptoMetadataEntityFields.DEVICE_KEYS_SENT_TO_SERVER, Boolean::class.java)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Version 12L added outbound group session persistence
|
||||||
|
private fun migrateTo12(realm: DynamicRealm) {
|
||||||
|
Timber.d("Step 11 -> 12")
|
||||||
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ 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.UserEntity
|
||||||
import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity
|
import org.matrix.android.sdk.internal.crypto.store.db.model.WithHeldSessionEntity
|
||||||
import io.realm.annotations.RealmModule
|
import io.realm.annotations.RealmModule
|
||||||
|
import org.matrix.android.sdk.internal.crypto.store.db.model.OutboundGroupSessionInfoEntity
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Realm module for Crypto store classes
|
* Realm module for Crypto store classes
|
||||||
@ -54,6 +55,7 @@ import io.realm.annotations.RealmModule
|
|||||||
OutgoingGossipingRequestEntity::class,
|
OutgoingGossipingRequestEntity::class,
|
||||||
MyDeviceLastSeenInfoEntity::class,
|
MyDeviceLastSeenInfoEntity::class,
|
||||||
WithHeldSessionEntity::class,
|
WithHeldSessionEntity::class,
|
||||||
SharedSessionEntity::class
|
SharedSessionEntity::class,
|
||||||
|
OutboundGroupSessionInfoEntity::class
|
||||||
])
|
])
|
||||||
internal class RealmCryptoStoreModule
|
internal class RealmCryptoStoreModule
|
||||||
|
@ -23,7 +23,12 @@ internal open class CryptoRoomEntity(
|
|||||||
@PrimaryKey var roomId: String? = null,
|
@PrimaryKey var roomId: String? = null,
|
||||||
var algorithm: String? = null,
|
var algorithm: String? = null,
|
||||||
var shouldEncryptForInvitedMembers: Boolean? = null,
|
var shouldEncryptForInvitedMembers: Boolean? = null,
|
||||||
var blacklistUnverifiedDevices: Boolean = false)
|
var blacklistUnverifiedDevices: 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
|
||||||
|
)
|
||||||
: RealmObject() {
|
: RealmObject() {
|
||||||
|
|
||||||
companion object
|
companion object
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
) : 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
|
||||||
|
}
|
@ -305,7 +305,8 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
|
|||||||
) {
|
) {
|
||||||
add(EventSharedAction.UseKeyBackup)
|
add(EventSharedAction.UseKeyBackup)
|
||||||
}
|
}
|
||||||
if (session.cryptoService().getCryptoDeviceInfo(session.myUserId).size > 1) {
|
if (session.cryptoService().getCryptoDeviceInfo(session.myUserId).size > 1
|
||||||
|
|| timelineEvent.senderInfo.userId != session.myUserId) {
|
||||||
add(EventSharedAction.ReRequestKey(timelineEvent.eventId))
|
add(EventSharedAction.ReRequestKey(timelineEvent.eventId))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user