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.
*/
override fun reRequestRoomKeyForEvent(event: Event) {
val wireContent = event.content!! // Wireeventcontent?
val wireContent = event.content!!
val algorithm = wireContent["algorithm"].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.
* It must be called in getEncryptingThreadHandler() thread.
* The callback is called in the UI thread.
*
* @param downloadUsers the user ids list
* @param callback the asynchronous callback
*/
private suspend fun doKeyDownloadForUsers(downloadUsers: MutableList<String>): Try<MXUsersDevicesMap<MXDeviceInfo>> {
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 timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
internal class IncomingRoomKeyRequestManager(
private val credentials: Credentials,
@ -46,19 +47,15 @@ internal class IncomingRoomKeyRequestManager(
/**
* 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.
*/
fun onRoomKeyRequestEvent(event: Event) {
suspend fun onRoomKeyRequestEvent(event: Event) {
val roomKeyShare = event.getClearContent().toModel<RoomKeyShare>()
when (roomKeyShare?.action) {
RoomKeyShare.ACTION_SHARE_REQUEST -> synchronized(receivedRoomKeyRequests) {
receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event))
}
RoomKeyShare.ACTION_SHARE_CANCELLATION -> synchronized(receivedRoomKeyRequestCancellations) {
receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event))
}
RoomKeyShare.ACTION_SHARE_REQUEST -> receivedRoomKeyRequests.add(IncomingRoomKeyRequest(event))
RoomKeyShare.ACTION_SHARE_CANCELLATION -> receivedRoomKeyRequestCancellations.add(IncomingRoomKeyRequestCancellation(event))
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
* current sync.
* It must be called on CryptoThread
*/
fun processReceivedRoomKeyRequests() {
var receivedRoomKeyRequests: List<IncomingRoomKeyRequest>? = null
synchronized(this.receivedRoomKeyRequests) {
if (this.receivedRoomKeyRequests.isNotEmpty()) {
receivedRoomKeyRequests = ArrayList(this.receivedRoomKeyRequests)
this.receivedRoomKeyRequests.clear()
}
}
if (null != receivedRoomKeyRequests) {
for (request in receivedRoomKeyRequests!!) {
val userId = request.userId!!
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
Timber.v("m.room_key_request from " + userId + ":" + deviceId + " for " + roomId + " / " + body.sessionId + " id " + request.requestId)
if (!TextUtils.equals(credentials.userId, userId)) {
if (userId == null || 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?
// 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)
@ -117,18 +100,16 @@ internal class IncomingRoomKeyRequestManager(
cryptoStore.deleteIncomingRoomKeyRequest(request)
continue
}
request.share = Runnable {
decryptor.shareKeysWithDevice(request)
cryptoStore.deleteIncomingRoomKeyRequest(request)
}
request.ignore = Runnable { 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 != null) {
if (device.isVerified) {
Timber.v("## processReceivedRoomKeyRequests() : device is already verified: sharing keys")
cryptoStore.deleteIncomingRoomKeyRequest(request)
@ -142,11 +123,9 @@ internal class IncomingRoomKeyRequestManager(
continue
}
}
cryptoStore.storeIncomingRoomKeyRequest(request)
onRoomKeyRequest(request)
}
}
var receivedRoomKeyRequestCancellations: List<IncomingRoomKeyRequestCancellation>? = null

View file

@ -194,25 +194,22 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
var senderKey: String? = event.getSenderKey()
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)) {
Timber.e("## onRoomKeyEvent() : Key event is missing fields")
return
}
if (event.getClearType() == EventType.FORWARDED_ROOM_KEY) {
Timber.v("## onRoomKeyEvent(), forward adding key : roomId " + roomKeyContent.roomId + " sessionId " + roomKeyContent.sessionId
+ " sessionKey " + roomKeyContent.sessionKey) // from " + event);
val forwardedRoomKeyContent = event.getClearContent().toModel<ForwardedRoomKeyContent>()!!
if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) {
forwarding_curve25519_key_chain = ArrayList()
forwardingCurve25519KeyChain = if (null == forwardedRoomKeyContent.forwardingCurve25519KeyChain) {
ArrayList()
} else {
forwarding_curve25519_key_chain = ArrayList(forwardedRoomKeyContent.forwardingCurve25519KeyChain!!)
ArrayList(forwardedRoomKeyContent.forwardingCurve25519KeyChain!!)
}
forwarding_curve25519_key_chain.add(senderKey!!)
forwardingCurve25519KeyChain.add(senderKey!!)
exportFormat = true
senderKey = forwardedRoomKeyContent.senderKey
@ -239,8 +236,7 @@ internal class MXMegolmDecryption(private val credentials: Credentials,
// inherit the claimed ed25519 key from the setup message
keysClaimed = event.getKeysClaimed().toMutableMap()
}
val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwarding_curve25519_key_chain!!, keysClaimed, exportFormat)
val added = olmDevice.addInboundGroupSession(roomKeyContent.sessionId!!, roomKeyContent.sessionKey!!, roomKeyContent.roomId!!, senderKey, forwardingCurve25519KeyChain, keysClaimed, exportFormat)
if (added) {
keysBackup.maybeBackupKeys()

View file

@ -146,10 +146,11 @@ internal class MXMegolmEncryption(
val userIds = ArrayList<String>()
var devicesCount = 0
for (userId in devicesByUsers.keys) {
val devicesList = devicesByUsers[userId]
devicesByUsers[userId]?.let {
userIds.add(userId)
subMap[userId] = devicesList!!
devicesCount += devicesList.size
subMap[userId] = it
devicesCount += it.size
}
if (devicesCount > 100) {
break
}
@ -157,7 +158,7 @@ internal class MXMegolmEncryption(
Timber.v("## shareKey() ; userId $userIds")
return shareUserDevicesKey(session, subMap)
.flatMap {
val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it) }
val remainingDevices = devicesByUsers.filterKeys { userIds.contains(it).not() }
shareKey(session, remainingDevices)
}
}

View file

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

View file

@ -118,7 +118,7 @@ internal class IncomingSASVerificationTransaction(
}
//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) {
Timber.e("## Failed to find device key ")

View file

@ -16,36 +16,47 @@
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.events.model.EventType
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.MXEventDecryptionResult
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.session.room.members.SenderRoomMemberExtractor
import io.realm.Realm
import timber.log.Timber
import java.util.*
internal class TimelineEventFactory(private val roomMemberExtractor: SenderRoomMemberExtractor,
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 {
val sender = eventEntity.sender
val cacheKey = sender + eventEntity.stateIndex
val senderData = cached.getOrPut(cacheKey) {
val senderData = senderCache.getOrPut(cacheKey) {
val senderRoomMember = roomMemberExtractor.extractFrom(eventEntity, realm)
SenderData(senderRoomMember?.displayName, senderRoomMember?.avatarUrl)
}
val event = eventEntity.asDomain()
if (event.getClearType() == EventType.ENCRYPTED) {
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)
} catch (e: Exception) {
Timber.e(e)
Timber.e(e, "Encrypted event: decryption failed")
if (e is MXDecryptionException) {
event.setCryptoError(e.cryptoError)
}
@ -62,7 +73,7 @@ internal class TimelineEventFactory(private val roomMemberExtractor: SenderRoomM
}
fun clear() {
cached.clear()
senderCache.clear()
}
private data class SenderData(

View file

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