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

@ -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,46 +63,32 @@ 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()
synchronized(this.receivedRoomKeyRequests) { for (request in roomKeyRequestsToProcess) {
if (this.receivedRoomKeyRequests.isNotEmpty()) { val userId = request.userId
receivedRoomKeyRequests = ArrayList(this.receivedRoomKeyRequests)
this.receivedRoomKeyRequests.clear()
}
}

if (null != receivedRoomKeyRequests) {
for (request in receivedRoomKeyRequests!!) {
val userId = request.userId!!
val deviceId = request.deviceId val deviceId = request.deviceId
val body = request.requestBody val body = request.requestBody
val roomId = body!!.roomId val roomId = body!!.roomId
val alg = body.algorithm val alg = body.algorithm


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

if (userId == null || credentials.userId != userId) {
if (!TextUtils.equals(credentials.userId, userId)) {
// TODO: determine if we sent this device the keys already: in // TODO: determine if we sent this device the keys already: in
Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now") Timber.e("## processReceivedRoomKeyRequests() : Ignoring room key request from other user for now")
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?

// if we don't have a decryptor for this room/alg, we don't have // 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. // the keys for the requested events, and can drop the requests.

val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg) val decryptor = roomDecryptorProvider.getRoomDecryptor(roomId, alg)

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

if (!decryptor.hasKeysForKeyRequest(request)) { if (!decryptor.hasKeysForKeyRequest(request)) {
Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session " + body.sessionId!!) Timber.e("## processReceivedRoomKeyRequests() : room key request for unknown session " + body.sessionId!!)
cryptoStore.deleteIncomingRoomKeyRequest(request) cryptoStore.deleteIncomingRoomKeyRequest(request)
@ -117,18 +100,16 @@ internal class IncomingRoomKeyRequestManager(
cryptoStore.deleteIncomingRoomKeyRequest(request) cryptoStore.deleteIncomingRoomKeyRequest(request)
continue continue
} }

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

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

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

if (device != null) {
if (null != device) {
if (device.isVerified) { if (device.isVerified) {
Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys") Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys")
cryptoStore.deleteIncomingRoomKeyRequest(request) cryptoStore.deleteIncomingRoomKeyRequest(request)
@ -142,11 +123,9 @@ internal class IncomingRoomKeyRequestManager(
continue continue
} }
} }

cryptoStore.storeIncomingRoomKeyRequest(request) cryptoStore.storeIncomingRoomKeyRequest(request)
onRoomKeyRequest(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()

View File

@ -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)
} }
} }

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")