Persist shared session info (enhance key reshare)
This commit is contained in:
parent
4ca0c23e2a
commit
102b8f88d0
@ -29,6 +29,8 @@ import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.api.session.room.model.RoomDirectoryVisibility
|
||||
import im.vector.matrix.android.api.session.room.model.create.CreateRoomParams
|
||||
import im.vector.matrix.android.common.CommonTestHelper
|
||||
import im.vector.matrix.android.common.CryptoTestHelper
|
||||
import im.vector.matrix.android.common.MockOkHttpInterceptor
|
||||
import im.vector.matrix.android.common.SessionTestParams
|
||||
import im.vector.matrix.android.common.TestConstants
|
||||
import im.vector.matrix.android.internal.crypto.GossipingRequestState
|
||||
@ -44,6 +46,8 @@ import junit.framework.TestCase.assertEquals
|
||||
import junit.framework.TestCase.assertNotNull
|
||||
import junit.framework.TestCase.assertTrue
|
||||
import junit.framework.TestCase.fail
|
||||
import kotlinx.coroutines.delay
|
||||
import okhttp3.internal.waitMillis
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
import org.junit.Test
|
||||
@ -56,6 +60,7 @@ import java.util.concurrent.CountDownLatch
|
||||
class KeyShareTests : InstrumentedTest {
|
||||
|
||||
private val mTestHelper = CommonTestHelper(context())
|
||||
private val mCryptoTestHelper = CryptoTestHelper(mTestHelper)
|
||||
|
||||
@Test
|
||||
fun test_DoNotSelfShareIfNotTrusted() {
|
||||
@ -234,6 +239,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
}
|
||||
if (tx.state == VerificationTxState.ShortCodeReady) {
|
||||
session1ShortCode = tx.getDecimalCodeRepresentation()
|
||||
Thread.sleep(500)
|
||||
tx.userHasVerifiedShortCode()
|
||||
}
|
||||
}
|
||||
@ -246,6 +252,7 @@ class KeyShareTests : InstrumentedTest {
|
||||
if (tx is SasVerificationTransaction) {
|
||||
if (tx.state == VerificationTxState.ShortCodeReady) {
|
||||
session2ShortCode = tx.getDecimalCodeRepresentation()
|
||||
Thread.sleep(500)
|
||||
tx.userHasVerifiedShortCode()
|
||||
}
|
||||
}
|
||||
|
@ -16,14 +16,18 @@
|
||||
|
||||
package im.vector.matrix.android.internal.crypto.gossiping
|
||||
|
||||
import android.util.Log
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import im.vector.matrix.android.InstrumentedTest
|
||||
import im.vector.matrix.android.api.session.crypto.MXCryptoError
|
||||
import im.vector.matrix.android.api.session.events.model.EventType
|
||||
import im.vector.matrix.android.api.session.events.model.toModel
|
||||
import im.vector.matrix.android.common.CommonTestHelper
|
||||
import im.vector.matrix.android.common.CryptoTestHelper
|
||||
import im.vector.matrix.android.common.MockOkHttpInterceptor
|
||||
import im.vector.matrix.android.common.SessionTestParams
|
||||
import im.vector.matrix.android.common.TestConstants
|
||||
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
|
||||
import im.vector.matrix.android.internal.crypto.model.event.WithHeldCode
|
||||
import org.junit.Assert
|
||||
import org.junit.FixMethodOrder
|
||||
@ -123,4 +127,77 @@ class WithHeldTests : InstrumentedTest {
|
||||
mTestHelper.signOutAndClose(bobSession)
|
||||
mTestHelper.signOutAndClose(bobUnverifiedSession)
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
fun test_WithHeldNoOlm() {
|
||||
val testData = mCryptoTestHelper.doE2ETestWithAliceAndBobInARoom()
|
||||
val aliceSession = testData.firstSession
|
||||
val bobSession = testData.secondSession!!
|
||||
val aliceInterceptor = mTestHelper.getTestInterceptor(aliceSession)
|
||||
|
||||
// Simulate no OTK
|
||||
aliceInterceptor!!.addRule(MockOkHttpInterceptor.SimpleRule(
|
||||
"/keys/claim",
|
||||
200,
|
||||
"""
|
||||
{ "one_time_keys" : {} }
|
||||
"""
|
||||
))
|
||||
Log.d("#TEST", "Recovery :${aliceSession.sessionParams.credentials.accessToken}")
|
||||
|
||||
val roomAlicePov = aliceSession.getRoom(testData.roomId)!!
|
||||
|
||||
// can we force one-time key shortage??
|
||||
|
||||
val eventId = mTestHelper.sendTextMessage(roomAlicePov, "first message", 1).first().eventId
|
||||
|
||||
// await for bob session to get the message
|
||||
mTestHelper.waitWithLatch { latch ->
|
||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId) != null
|
||||
}
|
||||
}
|
||||
|
||||
// Previous message should still be undecryptable (partially withheld session)
|
||||
val eventBobPOV = bobSession.getRoom(testData.roomId)?.getTimeLineEvent(eventId)
|
||||
try {
|
||||
// .. might need to wait a bit for stability?
|
||||
bobSession.cryptoService().decryptEvent(eventBobPOV!!.root, "")
|
||||
Assert.fail("This session should not be able to decrypt")
|
||||
} catch (failure: Throwable) {
|
||||
val type = (failure as MXCryptoError.Base).errorType
|
||||
val technicalMessage = failure.technicalMessage
|
||||
Assert.assertEquals("Error should be withheld", MXCryptoError.ErrorType.KEYS_WITHHELD, type)
|
||||
Assert.assertEquals("Cause should be unverified", WithHeldCode.NO_OLM.value, technicalMessage)
|
||||
}
|
||||
|
||||
// Ensure that alice has marked the session to be shared with bob
|
||||
val sessionId = eventBobPOV!!.root.content.toModel<EncryptedEventContent>()!!.sessionId!!
|
||||
val chainIndex = aliceSession.cryptoService().getSharedWithInfo(testData.roomId, sessionId).getObject(bobSession.myUserId, bobSession.sessionParams.credentials.deviceId)
|
||||
|
||||
Assert.assertEquals("Alice should have marked bob's device for this session", 0, chainIndex)
|
||||
// Add a new device for bob
|
||||
|
||||
aliceInterceptor.clearRules()
|
||||
val bobSecondSession = mTestHelper.logIntoAccount(bobSession.myUserId, SessionTestParams(withInitialSync = true))
|
||||
// send a second message
|
||||
val secondMessageId = mTestHelper.sendTextMessage(roomAlicePov, "second message", 1).first().eventId
|
||||
|
||||
// Check that the
|
||||
// await for bob SecondSession session to get the message
|
||||
mTestHelper.waitWithLatch { latch ->
|
||||
mTestHelper.retryPeriodicallyWithLatch(latch) {
|
||||
bobSecondSession.getRoom(testData.roomId)?.getTimeLineEvent(secondMessageId) != null
|
||||
}
|
||||
}
|
||||
|
||||
val chainIndex2 = aliceSession.cryptoService().getSharedWithInfo(testData.roomId, sessionId).getObject(bobSecondSession.myUserId, bobSecondSession.sessionParams.credentials.deviceId)
|
||||
|
||||
Assert.assertEquals("Alice should have marked bob's device for this session", 1, chainIndex2)
|
||||
|
||||
mTestHelper.signOutAndClose(bobSecondSession)
|
||||
testData.cleanUp(mTestHelper)
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -145,4 +145,7 @@ interface CryptoService {
|
||||
fun getIncomingRoomKeyRequests(): List<IncomingRoomKeyRequest>
|
||||
|
||||
fun getGossipingEventsTrail(): List<Event>
|
||||
|
||||
//For testing shared session
|
||||
fun getSharedWithInfo(roomId: String?, sessionId: String) : MXUsersDevicesMap<Int>
|
||||
}
|
||||
|
@ -809,7 +809,7 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
cryptoStore.saveGossipingEvent(event)
|
||||
onSecretSendReceived(event)
|
||||
}
|
||||
EventType.ROOM_KEY_WITHHELD -> {
|
||||
EventType.ROOM_KEY_WITHHELD -> {
|
||||
onKeyWithHeldReceived(event)
|
||||
}
|
||||
else -> {
|
||||
@ -839,7 +839,6 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
alg.onRoomKeyEvent(event, keysBackupService)
|
||||
}
|
||||
|
||||
|
||||
private fun onKeyWithHeldReceived(event: Event) {
|
||||
val withHeldContent = event.getClearContent().toModel<RoomKeyWithHeldContent>() ?: return Unit.also {
|
||||
Timber.e("## CRYPTO | Malformed onKeyWithHeldReceived() : missing fields")
|
||||
@ -852,7 +851,6 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
Timber.e("## CRYPTO | onKeyWithHeldReceived() : Unable to handle WithHeldContent for ${withHeldContent.algorithm}")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun onSecretSendReceived(event: Event) {
|
||||
@ -1332,6 +1330,10 @@ internal class DefaultCryptoService @Inject constructor(
|
||||
return cryptoStore.getGossipingEventsTrail()
|
||||
}
|
||||
|
||||
override fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int> {
|
||||
return cryptoStore.getSharedWithInfo(roomId, sessionId)
|
||||
}
|
||||
|
||||
/* ==========================================================================================
|
||||
* For test only
|
||||
* ========================================================================================== */
|
||||
|
@ -119,7 +119,7 @@ internal class MXMegolmEncryption(
|
||||
|
||||
defaultKeysBackupService.maybeBackupKeys()
|
||||
|
||||
return MXOutboundSessionInfo(sessionId)
|
||||
return MXOutboundSessionInfo(sessionId, SharedWithHelper(roomId, sessionId, cryptoStore))
|
||||
}
|
||||
|
||||
/**
|
||||
@ -145,7 +145,7 @@ internal class MXMegolmEncryption(
|
||||
val deviceIds = devicesInRoom.getUserDeviceIds(userId)
|
||||
for (deviceId in deviceIds!!) {
|
||||
val deviceInfo = devicesInRoom.getObject(userId, deviceId)
|
||||
if (deviceInfo != null && null == safeSession.sharedWithDevices.getObject(userId, deviceId)) {
|
||||
if (deviceInfo != null && !cryptoStore.wasSessionSharedWithUser(roomId, safeSession.sessionId, userId, deviceId).found) {
|
||||
val devices = shareMap.getOrPut(userId) { ArrayList() }
|
||||
devices.add(deviceInfo)
|
||||
}
|
||||
@ -247,7 +247,7 @@ internal class MXMegolmEncryption(
|
||||
// for dead devices on every message.
|
||||
for ((userId, devicesToShareWith) in devicesByUser) {
|
||||
for ((deviceId) in devicesToShareWith) {
|
||||
session.sharedWithDevices.setObject(userId, deviceId, chainIndex)
|
||||
session.sharedWithHelper.markedSessionAsShared(userId, deviceId, chainIndex)
|
||||
}
|
||||
}
|
||||
|
||||
@ -382,7 +382,7 @@ internal class MXMegolmEncryption(
|
||||
.also { Timber.w("Device not found") }
|
||||
|
||||
// Get the chain index of the key we previously sent this device
|
||||
val chainIndex = outboundSession?.sharedWithDevices?.getObject(userId, deviceId)?.toLong() ?: return false
|
||||
val chainIndex = outboundSession?.sharedWithHelper?.wasSharedWith(userId,deviceId) ?: return false
|
||||
.also { Timber.w("[MXMegolmEncryption] reshareKey : ERROR : Never share megolm with this device") }
|
||||
|
||||
val devicesByUser = mapOf(userId to listOf(deviceInfo))
|
||||
@ -401,7 +401,7 @@ internal class MXMegolmEncryption(
|
||||
.fold(
|
||||
{
|
||||
// TODO
|
||||
payloadJson["content"] = it.exportKeys(chainIndex) ?: ""
|
||||
payloadJson["content"] = it.exportKeys(chainIndex.toLong()) ?: ""
|
||||
},
|
||||
{
|
||||
// TODO
|
||||
|
@ -23,17 +23,14 @@ import timber.log.Timber
|
||||
|
||||
internal class MXOutboundSessionInfo(
|
||||
// The id of the session
|
||||
val sessionId: String) {
|
||||
val sessionId: String,
|
||||
val sharedWithHelper: SharedWithHelper) {
|
||||
// When the session was created
|
||||
private val creationTime = System.currentTimeMillis()
|
||||
|
||||
// Number of times this session has been used
|
||||
var useCount: Int = 0
|
||||
|
||||
// Devices with which we have shared the session key
|
||||
// userId -> {deviceId -> msgindex}
|
||||
val sharedWithDevices: MXUsersDevicesMap<Int> = MXUsersDevicesMap()
|
||||
|
||||
fun needsRotation(rotationPeriodMsgs: Int, rotationPeriodMs: Int): Boolean {
|
||||
var needsRotation = false
|
||||
val sessionLifetime = System.currentTimeMillis() - creationTime
|
||||
@ -53,6 +50,7 @@ internal class MXOutboundSessionInfo(
|
||||
* @return true if we have shared the session with devices which aren't in devicesInRoom.
|
||||
*/
|
||||
fun sharedWithTooManyDevices(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): Boolean {
|
||||
val sharedWithDevices = sharedWithHelper.sharedWithDevices()
|
||||
val userIds = sharedWithDevices.userIds
|
||||
|
||||
for (userId in userIds) {
|
||||
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (c) 2020 New Vector Ltd
|
||||
*
|
||||
* 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 im.vector.matrix.android.internal.crypto.algorithms.megolm
|
||||
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
|
||||
|
||||
internal class SharedWithHelper(
|
||||
private val roomId: String,
|
||||
private val sessionId: String,
|
||||
private val cryptoStore: IMXCryptoStore) {
|
||||
|
||||
fun sharedWithDevices(): MXUsersDevicesMap<Int> {
|
||||
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) {
|
||||
cryptoStore.markedSessionAsShared(roomId, sessionId, userId, deviceId, chainIndex)
|
||||
}
|
||||
}
|
@ -31,6 +31,7 @@ import im.vector.matrix.android.internal.crypto.OutgoingRoomKeyRequest
|
||||
import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
|
||||
import im.vector.matrix.android.internal.crypto.model.event.RoomKeyWithHeldContent
|
||||
@ -421,6 +422,10 @@ 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 wasSessionSharedWithUser(roomId: String?, sessionId: String, userId: String, deviceId: String) : SharedSessionResult
|
||||
data class SharedSessionResult(val found: Boolean, val chainIndex: Int?)
|
||||
fun getSharedWithInfo(roomId: String?, sessionId: String) : MXUsersDevicesMap<Int>
|
||||
// Dev tools
|
||||
|
||||
fun getOutgoingRoomKeyRequests(): List<OutgoingRoomKeyRequest>
|
||||
|
@ -39,10 +39,10 @@ import im.vector.matrix.android.internal.crypto.OutgoingSecretRequest
|
||||
import im.vector.matrix.android.internal.crypto.algorithms.olm.OlmDecryptionResult
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoCrossSigningKey
|
||||
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmInboundGroupSessionWrapper2
|
||||
import im.vector.matrix.android.internal.crypto.model.OlmSessionWrapper
|
||||
import im.vector.matrix.android.internal.crypto.model.event.RoomKeyWithHeldContent
|
||||
import im.vector.matrix.android.internal.crypto.model.event.WithHeldCode
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
|
||||
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
|
||||
import im.vector.matrix.android.internal.crypto.model.toEntity
|
||||
@ -69,11 +69,13 @@ import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.SharedSessionEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.WithHeldSessionEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.createPrimaryKey
|
||||
import im.vector.matrix.android.internal.crypto.store.db.query.create
|
||||
import im.vector.matrix.android.internal.crypto.store.db.query.delete
|
||||
import im.vector.matrix.android.internal.crypto.store.db.query.get
|
||||
import im.vector.matrix.android.internal.crypto.store.db.query.getById
|
||||
@ -1459,4 +1461,40 @@ internal class RealmCryptoStore @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun markedSessionAsShared(roomId: String?, sessionId: String, userId: String, deviceId: String, chainIndex: Int) {
|
||||
doRealmTransaction(realmConfiguration) { realm ->
|
||||
SharedSessionEntity.create(
|
||||
realm = realm,
|
||||
roomId = roomId,
|
||||
sessionId = sessionId,
|
||||
userId = userId,
|
||||
deviceId = deviceId,
|
||||
chainIndex = chainIndex
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun wasSessionSharedWithUser(roomId: String?, sessionId: String, userId: String, deviceId: String): IMXCryptoStore.SharedSessionResult {
|
||||
return doWithRealm(realmConfiguration) { realm ->
|
||||
SharedSessionEntity.get(realm, roomId, sessionId, userId, deviceId)?.let {
|
||||
IMXCryptoStore.SharedSessionResult(true, it.chainIndex)
|
||||
} ?: IMXCryptoStore.SharedSessionResult(false, null)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSharedWithInfo(roomId: String?, sessionId: String): MXUsersDevicesMap<Int> {
|
||||
return doWithRealm(realmConfiguration) { realm ->
|
||||
val result = MXUsersDevicesMap<Int>()
|
||||
SharedSessionEntity.get(realm, roomId, sessionId)
|
||||
.groupBy { it.userId }
|
||||
.forEach { (userId, shared) ->
|
||||
shared.forEach {
|
||||
result.setObject(userId, it.deviceId, it.chainIndex)
|
||||
}
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ import im.vector.matrix.android.internal.crypto.store.db.model.MyDeviceLastSeenI
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.SharedSessionEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntityFields
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.WithHeldSessionEntityFields
|
||||
@ -431,5 +432,18 @@ internal class RealmCryptoStoreMigration @Inject constructor(private val crossSi
|
||||
.addIndex(WithHeldSessionEntityFields.SENDER_KEY)
|
||||
.addField(WithHeldSessionEntityFields.CODE_STRING, String::class.java)
|
||||
.addField(WithHeldSessionEntityFields.REASON, String::class.java)
|
||||
|
||||
|
||||
realm.schema.create("SharedSessionEntity")
|
||||
.addField(SharedSessionEntityFields.ROOM_ID, String::class.java)
|
||||
.addField(SharedSessionEntityFields.ALGORITHM, String::class.java)
|
||||
.addField(SharedSessionEntityFields.SESSION_ID, String::class.java)
|
||||
.addIndex(SharedSessionEntityFields.SESSION_ID)
|
||||
.addField(SharedSessionEntityFields.USER_ID, String::class.java)
|
||||
.addIndex(SharedSessionEntityFields.USER_ID)
|
||||
.addField(SharedSessionEntityFields.DEVICE_ID, String::class.java)
|
||||
.addIndex(SharedSessionEntityFields.DEVICE_ID)
|
||||
.addField(SharedSessionEntityFields.CHAIN_INDEX, Long::class.java)
|
||||
.setNullable(SharedSessionEntityFields.CHAIN_INDEX, true)
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import im.vector.matrix.android.internal.crypto.store.db.model.MyDeviceLastSeenI
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OlmInboundGroupSessionEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OlmSessionEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.SharedSessionEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.UserEntity
|
||||
import im.vector.matrix.android.internal.crypto.store.db.model.WithHeldSessionEntity
|
||||
@ -52,6 +53,7 @@ import io.realm.annotations.RealmModule
|
||||
IncomingGossipingRequestEntity::class,
|
||||
OutgoingGossipingRequestEntity::class,
|
||||
MyDeviceLastSeenInfoEntity::class,
|
||||
WithHeldSessionEntity::class
|
||||
WithHeldSessionEntity::class,
|
||||
SharedSessionEntity::class
|
||||
])
|
||||
internal class RealmCryptoStoreModule
|
||||
|
Loading…
x
Reference in New Issue
Block a user