Crypto: finally get a working encrypt/decrypt + SAS

This commit is contained in:
ganfra 2019-06-06 19:10:04 +02:00
parent 6b0ab10231
commit c4d7711d2f
9 changed files with 141 additions and 151 deletions

View File

@ -958,7 +958,7 @@ internal class CryptoManager(
setRoomBlacklistUnverifiedDevices(roomId, false) setRoomBlacklistUnverifiedDevices(roomId, false)
} }


// TODO Check if this method is still necessary // TODO Check if this method is still necessary
/** /**
* Cancel any earlier room key request * Cancel any earlier room key request
* *
@ -974,7 +974,7 @@ internal class CryptoManager(
* @param event the event to decrypt again. * @param event the event to decrypt again.
*/ */
override fun reRequestRoomKeyForEvent(event: Event) { override fun reRequestRoomKeyForEvent(event: Event) {
val wireContent = event.content!! // Wireeventcontent? val wireContent = event.content!!


val algorithm = wireContent["algorithm"].toString() val algorithm = wireContent["algorithm"].toString()
val senderKey = wireContent["sender_key"].toString() val senderKey = wireContent["sender_key"].toString()

View File

@ -280,11 +280,8 @@ internal class DeviceListManager(private val cryptoStore: IMXCryptoStore,


/** /**
* Download the devices keys for a set of users. * Download the devices keys for a set of users.
* It must be called in getEncryptingThreadHandler() thread.
* The callback is called in the UI thread.
* *
* @param downloadUsers the user ids list * @param downloadUsers the user ids list
* @param callback the asynchronous callback
*/ */
private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList<String>): Try<MXUsersDevicesMap<MXDeviceInfo>> { private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList<String>): Try<MXUsersDevicesMap<MXDeviceInfo>> {
Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers") Timber.v("## doKeyDownloadForUsers() : doKeyDownloadForUsers $downloadUsers")

View File

@ -25,6 +25,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyShare
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
import kotlin.collections.ArrayList


internal class IncomingRoomKeyRequestManager( internal class IncomingRoomKeyRequestManager(
private val credentials: Credentials, private val credentials: Credentials,
@ -46,19 +47,15 @@ internal class IncomingRoomKeyRequestManager(


/** /**
* Called when we get an m.room_key_request event * Called when we get an m.room_key_request event
* This method must be called on getEncryptingThreadHandler() thread. * It must be called on CryptoThread
* *
* @param event the announcement event. * @param event the announcement event.
*/ */
fun onRoomKeyRequestEvent(event: Event) { suspend fun onRoomKeyRequestEvent(event: Event) {
val roomKeyShare = event.getClearContent().toModel<RoomKeyShare>() val roomKeyShare = event.getClearContent().toModel<RoomKeyShare>()
when (roomKeyShare?.action) { when (roomKeyShare?.action) {
RoomKeyShare.ACTION_SHARE_REQUEST -> synchronized(receivedRoomKeyRequests) { RoomKeyShare.ACTION_SHARE_REQUEST -> receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event))
receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event)) RoomKeyShare.ACTION_SHARE_CANCELLATION -> receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event))
}
RoomKeyShare.ACTION_SHARE_CANCELLATION -> synchronized(receivedRoomKeyRequestCancellations) {
receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event))
}
else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action " + roomKeyShare?.action) else -> Timber.e("## onRoomKeyRequestEvent() : unsupported action " + roomKeyShare?.action)
} }
} }
@ -66,86 +63,68 @@ internal class IncomingRoomKeyRequestManager(
/** /**
* Process any m.room_key_request events which were queued up during the * Process any m.room_key_request events which were queued up during the
* current sync. * current sync.
* It must be called on CryptoThread
*/ */
fun processReceivedRoomKeyRequests() { fun processReceivedRoomKeyRequests() {
var receivedRoomKeyRequests: List<IncomingRoomKeyRequest>? = null val roomKeyRequestsToProcess = ArrayList(receivedRoomKeyRequests)
receivedRoomKeyRequests.clear()
for (request in roomKeyRequestsToProcess) {
val userId = request.userId
val deviceId = request.deviceId
val body = request.requestBody
val roomId = body!!.roomId
val alg = body.algorithm


synchronized(this.receivedRoomKeyRequests) { Timber.v("m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.sessionId + " id " + request.requestId)
if (this.receivedRoomKeyRequests.isNotEmpty()) { if (userId == null || credentials.userId != userId) {
receivedRoomKeyRequests = ArrayList(this.receivedRoomKeyRequests) // TODO: determine if we sent this device the keys already: in
this.receivedRoomKeyRequests.clear() Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now")
return
} }
} // todo: should we queue up requests we don't yet have keys for, in case they turn up later?

// if we don't have a decryptor for this room/alg, we don't have
if (null != receivedRoomKeyRequests) { // the keys for the requested events, and can drop the requests.
for (request in receivedRoomKeyRequests!!) { val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg)
val userId = request.userId!! if (null == decryptor) {
val deviceId = request.deviceId Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown $alg in room $roomId")
val body = request.requestBody continue
val roomId = body!!.roomId
val alg = body.algorithm

Timber.v("m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.sessionId + " id " + request.requestId)

if (!TextUtils.equals(credentials.userId, userId)) {
// TODO: determine if we sent this device the keys already: in
Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now")
return
}

// todo: should we queue up requests we don't yet have keys for,
// in case they turn up later?

// if we don't have a decryptor for this room/alg, we don't have
// the keys for the requested events, and can drop the requests.

val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg)

if (null == decryptor) {
Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown $alg in room $roomId")
continue
}

if (!decryptor.hasKeysForKeyRequest(request)) {
Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session " + body.sessionId!!)
cryptoStore.deleteIncomingRoomKeyRequest(request)
continue
}

if (TextUtils.equals(deviceId, credentials.deviceId) && TextUtils.equals(credentials.userId, userId)) {
Timber.v("## processReceivedRoomKeyRequests() : oneself device - ignored")
cryptoStore.deleteIncomingRoomKeyRequest(request)
continue
}

request.share = Runnable {
decryptor.shareKeysWithDevice(request)
cryptoStore.deleteIncomingRoomKeyRequest(request)
}

request.ignore = Runnable { cryptoStore.deleteIncomingRoomKeyRequest(request) }

// if the device is verified already, share the keys
val device = cryptoStore.getUserDevice(deviceId!!, userId)

if (null != device) {
if (device.isVerified) {
Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys")
cryptoStore.deleteIncomingRoomKeyRequest(request)
request.share?.run()
continue
}

if (device.isBlocked) {
Timber.v("## processReceivedRoomKeyRequests() : device is blocked -> ignored")
cryptoStore.deleteIncomingRoomKeyRequest(request)
continue
}
}

cryptoStore.storeIncomingRoomKeyRequest(request)
onRoomKeyRequest(request)
} }
if (!decryptor.hasKeysForKeyRequest(request)) {
Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session " + body.sessionId!!)
cryptoStore.deleteIncomingRoomKeyRequest(request)
continue
}

if (TextUtils.equals(deviceId, credentials.deviceId) && TextUtils.equals(credentials.userId, userId)) {
Timber.v("## processReceivedRoomKeyRequests() : oneself device - ignored")
cryptoStore.deleteIncomingRoomKeyRequest(request)
continue
}
request.share = Runnable {
decryptor.shareKeysWithDevice(request)
cryptoStore.deleteIncomingRoomKeyRequest(request)
}
request.ignore = Runnable {
cryptoStore.deleteIncomingRoomKeyRequest(request)
}
// if the device is verified already, share the keys
val device = cryptoStore.getUserDevice(deviceId!!, userId)
if (device != null) {
if (device.isVerified) {
Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys")
cryptoStore.deleteIncomingRoomKeyRequest(request)
request.share?.run()
continue
}

if (device.isBlocked) {
Timber.v("## processReceivedRoomKeyRequests() : device is blocked -> ignored")
cryptoStore.deleteIncomingRoomKeyRequest(request)
continue
}
}
cryptoStore.storeIncomingRoomKeyRequest(request)
onRoomKeyRequest(request)
} }


var receivedRoomKeyRequestCancellations: List<IncomingRoomKeyRequestCancellation>? = null var receivedRoomKeyRequestCancellations: List<IncomingRoomKeyRequestCancellation>? = null

View File

@ -194,25 +194,22 @@ internal class MXMegolmDecryption(private val credentials: Credentials,


var senderKey: String? = event.getSenderKey() var senderKey: String? = event.getSenderKey()
var keysClaimed: MutableMap<String, String> = HashMap() var keysClaimed: MutableMap<String, String> = HashMap()
var forwarding_curve25519_key_chain: MutableList<String>? = null var forwardingCurve25519KeyChain: MutableList<String> = ArrayList()


if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.sessionId) || TextUtils.isEmpty(roomKeyContent.sessionKey)) { if (TextUtils.isEmpty(roomKeyContent.roomId) || TextUtils.isEmpty(roomKeyContent.sessionId) || TextUtils.isEmpty(roomKeyContent.sessionKey)) {
Timber.e("## onRoomKeyEvent() : Key event is missing fields") Timber.e("## onRoomKeyEvent() : Key event is missing fields")
return return
} }

if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) { if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
Timber.v("## onRoomKeyEvent(), forward adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId Timber.v("## onRoomKeyEvent(), forward adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId
+ " sessionKey " + roomKeyContent.sessionKey) // from " + event); + " sessionKey " + roomKeyContent.sessionKey) // from " + event);
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()!! val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()!!

forwardingCurve25519KeyChain = if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) {
if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) { ArrayList()
forwarding_curve25519_key_chain = ArrayList()
} else { } else {
forwarding_curve25519_key_chain = ArrayList(forwardedRoomKeyContent.forwardingCurve25519KeyChain!!) ArrayList(forwardedRoomKeyContent.forwardingCurve25519KeyChain!!)
} }

forwardingCurve25519KeyChain.add(senderKey!!)
forwarding_curve25519_key_chain.add(senderKey!!)


exportFormat = true exportFormat = true
senderKey = forwardedRoomKeyContent.senderKey senderKey = forwardedRoomKeyContent.senderKey
@ -239,8 +236,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
// inherit the claimed ed25519 key from the setup message // inherit the claimed ed25519 key from the setup message
keysClaimed = event.getKeysClaimed().toMutableMap() keysClaimed = event.getKeysClaimed().toMutableMap()
} }

val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwardingCurve25519KeyChain, keysClaimed, exportFormat)
val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwarding_curve25519_key_chain!!, keysClaimed, exportFormat)


if (added) { if (added) {
keysBackup.maybeBackupKeys() keysBackup.maybeBackupKeys()
@ -318,41 +314,41 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
deviceListManager deviceListManager
.downloadKeys(listOf(userId), false) .downloadKeys(listOf(userId), false)
.flatMap { .flatMap {
val deviceId = request.deviceId val deviceId = request.deviceId
val deviceInfo = cryptoStore.getUserDevice(deviceId!!, userId) val deviceInfo = cryptoStore.getUserDevice(deviceId!!, userId)
if (deviceInfo == null) { if (deviceInfo == null) {
throw RuntimeException() throw RuntimeException()
} else { } else {
val devicesByUser = HashMap<String, List<MXDeviceInfo>>() val devicesByUser = HashMap<String, List<MXDeviceInfo>>()
devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo)) devicesByUser[userId] = ArrayList(Arrays.asList(deviceInfo))
ensureOlmSessionsForDevicesAction ensureOlmSessionsForDevicesAction
.handle(devicesByUser) .handle(devicesByUser)
.flatMap { .flatMap {
val body = request.requestBody val body = request.requestBody
val olmSessionResult = it.getObject(deviceId, userId) val olmSessionResult = it.getObject(deviceId, userId)
if (olmSessionResult?.mSessionId == null) { if (olmSessionResult?.mSessionId == null) {
// no session with this device, probably because there // no session with this device, probably because there
// were no one-time keys. // were no one-time keys.
Try.just(Unit) Try.just(Unit)
}
Timber.v("## shareKeysWithDevice() : sharing keys for session " + body!!.senderKey + "|" + body.sessionId
+ " with device " + userId + ":" + deviceId)
val inboundGroupSession = olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId)

val payloadJson = HashMap<String, Any>()
payloadJson["type"] = EventType.FORWARDED_ROOM_KEY
payloadJson["content"] = inboundGroupSession!!.exportKeys()!!

val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
val sendToDeviceMap = MXUsersDevicesMap<Any>()
sendToDeviceMap.setObject(encodedPayload, userId, deviceId)
Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
sendToDeviceTask.execute(sendToDeviceParams)
} }
Timber.v("## shareKeysWithDevice() : sharing keys for session " + body!!.senderKey + "|" + body.sessionId
+ " with device " + userId + ":" + deviceId)
val inboundGroupSession = olmDevice.getInboundGroupSession(body.sessionId, body.senderKey, body.roomId)

val payloadJson = HashMap<String, Any>()
payloadJson["type"] = EventType.FORWARDED_ROOM_KEY
payloadJson["content"] = inboundGroupSession!!.exportKeys()!!

val encodedPayload = messageEncrypter.encryptMessage(payloadJson, Arrays.asList(deviceInfo))
val sendToDeviceMap = MXUsersDevicesMap<Any>()
sendToDeviceMap.setObject(encodedPayload, userId, deviceId)
Timber.v("## shareKeysWithDevice() : sending to $userId:$deviceId")
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, sendToDeviceMap)
sendToDeviceTask.execute(sendToDeviceParams)
}




} }
} }
} }
} }

View File

@ -88,7 +88,7 @@ internal class MXMegolmEncryption(
keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!! keysClaimedMap["ed25519"] = olmDevice.deviceEd25519Key!!


olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!, olmDevice.addInboundGroupSession(sessionId!!, olmDevice.getSessionKey(sessionId)!!, roomId, olmDevice.deviceCurve25519Key!!,
ArrayList(), keysClaimedMap, false) ArrayList(), keysClaimedMap, false)


keysBackup.maybeBackupKeys() keysBackup.maybeBackupKeys()


@ -103,10 +103,10 @@ internal class MXMegolmEncryption(
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): Try<MXOutboundSessionInfo> { private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<MXDeviceInfo>): Try<MXOutboundSessionInfo> {
var session = outboundSession var session = outboundSession
if (session == null if (session == null
// Need to make a brand new session? // Need to make a brand new session?
|| session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) || session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs)
// Determine if we have shared with anyone we shouldn't have // Determine if we have shared with anyone we shouldn't have
|| session.sharedWithTooManyDevices(devicesInRoom)) { || session.sharedWithTooManyDevices(devicesInRoom)) {
session = prepareNewSessionInRoom() session = prepareNewSessionInRoom()
outboundSession = session outboundSession = session
} }
@ -146,10 +146,11 @@ internal class MXMegolmEncryption(
val userIds = ArrayList<String>() val userIds = ArrayList<String>()
var devicesCount = 0 var devicesCount = 0
for (userId in devicesByUsers.keys) { for (userId in devicesByUsers.keys) {
val devicesList = devicesByUsers[userId] devicesByUsers[userId]?.let {
userIds.add(userId) userIds.add(userId)
subMap[userId] = devicesList!! subMap[userId] = it
devicesCount += devicesList.size devicesCount += it.size
}
if (devicesCount > 100) { if (devicesCount > 100) {
break break
} }
@ -157,7 +158,7 @@ internal class MXMegolmEncryption(
Timber.v("## shareKey() ; userId $userIds") Timber.v("## shareKey() ; userId $userIds")
return shareUserDevicesKey(session, subMap) return shareUserDevicesKey(session, subMap)
.flatMap { .flatMap {
val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it) } val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() }
shareKey(session, remainingDevices) shareKey(session, remainingDevices)
} }
} }
@ -191,7 +192,7 @@ internal class MXMegolmEncryption(
return ensureOlmSessionsForDevicesAction.handle(devicesByUser) return ensureOlmSessionsForDevicesAction.handle(devicesByUser)
.flatMap { .flatMap {
Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after " Timber.v("## shareUserDevicesKey() : ensureOlmSessionsForDevices succeeds after "
+ (System.currentTimeMillis() - t0) + " ms") + (System.currentTimeMillis() - t0) + " ms")
val contentMap = MXUsersDevicesMap<Any>() val contentMap = MXUsersDevicesMap<Any>()
var haveTargets = false var haveTargets = false
val userIds = it.userIds val userIds = it.userIds
@ -226,7 +227,7 @@ internal class MXMegolmEncryption(
sendToDeviceTask.execute(sendToDeviceParams) sendToDeviceTask.execute(sendToDeviceParams)
.map { .map {
Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after " Timber.v("## shareUserDevicesKey() : sendToDevice succeeds after "
+ (System.currentTimeMillis() - t0) + " ms") + (System.currentTimeMillis() - t0) + " ms")


// Add the devices we have shared with to session.sharedWithDevices. // Add the devices we have shared with to session.sharedWithDevices.
// we deliberately iterate over devicesByUser (ie, the devices we // we deliberately iterate over devicesByUser (ie, the devices we
@ -292,7 +293,7 @@ internal class MXMegolmEncryption(
.downloadKeys(userIds, false) .downloadKeys(userIds, false)
.map { .map {
val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices() val encryptToVerifiedDevicesOnly = cryptoStore.getGlobalBlacklistUnverifiedDevices()
|| cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId) || cryptoStore.getRoomsListBlacklistUnverifiedDevices().contains(roomId)


val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>() val devicesInRoom = MXUsersDevicesMap<MXDeviceInfo>()
val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>() val unknownDevices = MXUsersDevicesMap<MXDeviceInfo>()

View File

@ -223,7 +223,7 @@ internal class DefaultSasVerificationService(private val mCredentials: Credentia
.fold( .fold(
{ error() }, { error() },
{ {
if (it != null && it.getUserDeviceIds(otherUserId).contains(startReq.fromDevice)) { if (it.getUserDeviceIds(otherUserId).contains(startReq.fromDevice)) {
success(it) success(it)
} else { } else {
error() error()

View File

@ -118,7 +118,7 @@ internal class IncomingSASVerificationTransaction(
} }


//Bobs device ensures that it has a copy of Alices device key. //Bobs device ensures that it has a copy of Alices device key.
val mxDeviceInfo = mCryptoStore.getUserDevice(this.otherUserId, otherDeviceId!!) val mxDeviceInfo = mCryptoStore.getUserDevice(deviceId = otherDeviceId!!, userId = otherUserId)


if (mxDeviceInfo?.fingerprint() == null) { if (mxDeviceInfo?.fingerprint() == null) {
Timber.e("## Failed to find device key ") Timber.e("## Failed to find device key ")

View File

@ -16,36 +16,47 @@


package im.vector.matrix.android.internal.session.room.timeline package im.vector.matrix.android.internal.session.room.timeline


import im.vector.matrix.android.api.failure.Failure
import im.vector.matrix.android.api.session.crypto.CryptoService import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.MXDecryptionException import im.vector.matrix.android.internal.crypto.MXDecryptionException
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor
import io.realm.Realm import io.realm.Realm
import timber.log.Timber import timber.log.Timber
import java.util.*


internal class TimelineEventFactory(private val roomMemberExtractor: SenderRoomMemberExtractor, internal class TimelineEventFactory(private val roomMemberExtractor: SenderRoomMemberExtractor,
private val cryptoService: CryptoService) { private val cryptoService: CryptoService) {


private val cached = mutableMapOf<String, SenderData>() private val timelineId = UUID.randomUUID().toString()
private val senderCache = mutableMapOf<String, SenderData>()
private val decryptionCache = mutableMapOf<String, MXEventDecryptionResult>()


fun create(eventEntity: EventEntity, realm: Realm = eventEntity.realm): TimelineEvent { fun create(eventEntity: EventEntity, realm: Realm = eventEntity.realm): TimelineEvent {
val sender = eventEntity.sender val sender = eventEntity.sender
val cacheKey = sender + eventEntity.stateIndex val cacheKey = sender + eventEntity.stateIndex
val senderData = cached.getOrPut(cacheKey) { val senderData = senderCache.getOrPut(cacheKey) {
val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm) val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm)
SenderData(senderRoomMember?.displayName, senderRoomMember?.avatarUrl) SenderData(senderRoomMember?.displayName, senderRoomMember?.avatarUrl)
} }
val event = eventEntity.asDomain() val event = eventEntity.asDomain()
if (event.getClearType() == EventType.ENCRYPTED) { if (event.getClearType() == EventType.ENCRYPTED) {
try { try {
val result = cryptoService.decryptEvent(event, "TODO") Timber.v("Encrypted event: try to decrypt ${event.eventId}")
val result = if (decryptionCache.containsKey(eventEntity.localId)) {
Timber.v("Encrypted event ${event.eventId} cached")
decryptionCache[eventEntity.localId]
} else {
cryptoService.decryptEvent(event, timelineId)?.also {
decryptionCache[eventEntity.localId] = it
}
}
event.setClearData(result) event.setClearData(result)
} catch (e: Exception) { } catch (e: Exception) {
Timber.e(e) Timber.e(e, "Encrypted event: decryption failed")
if (e is MXDecryptionException) { if (e is MXDecryptionException) {
event.setCryptoError(e.cryptoError) event.setCryptoError(e.cryptoError)
} }
@ -62,7 +73,7 @@ internal class TimelineEventFactory(private val roomMemberExtractor: SenderRoomM
} }


fun clear() { fun clear() {
cached.clear() senderCache.clear()
} }


private data class SenderData( private data class SenderData(

View File

@ -34,22 +34,28 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
val measure = measureTimeMillis { val measure = measureTimeMillis {
// Handle the to device events before the room ones // Handle the to device events before the room ones
// to ensure to decrypt them properly // to ensure to decrypt them properly
Timber.v("Handle toDevice")
if (syncResponse.toDevice != null) { if (syncResponse.toDevice != null) {
cryptoSyncHandler.handleToDevice(syncResponse.toDevice) cryptoSyncHandler.handleToDevice(syncResponse.toDevice)
} }
Timber.v("Handle rooms")
if (syncResponse.rooms != null) { if (syncResponse.rooms != null) {
roomSyncHandler.handle(syncResponse.rooms) roomSyncHandler.handle(syncResponse.rooms)
} }
Timber.v("Handle groups")
if (syncResponse.groups != null) { if (syncResponse.groups != null) {
groupSyncHandler.handle(syncResponse.groups) groupSyncHandler.handle(syncResponse.groups)
} }
Timber.v("Handle accoundData")
if (syncResponse.accountData != null) { if (syncResponse.accountData != null) {
userAccountDataSyncHandler.handle(syncResponse.accountData) userAccountDataSyncHandler.handle(syncResponse.accountData)
} }
Timber.v("On sync completed")
cryptoSyncHandler.onSyncCompleted(syncResponse, fromToken, isCatchingUp) cryptoSyncHandler.onSyncCompleted(syncResponse, fromToken, isCatchingUp)
} }
val isInitialSync = fromToken == null val isInitialSync = fromToken == null
if (!cryptoManager.isStarted()) { if (!cryptoManager.isStarted()) {
Timber.v("Should start cryptoManager")
cryptoManager.start(isInitialSync) cryptoManager.start(isInitialSync)
} }
Timber.v("Finish handling sync in $measure ms") Timber.v("Finish handling sync in $measure ms")