mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 11:59:12 +03:00
Crypto: finally get a working encrypt/decrypt + SAS
This commit is contained in:
parent
6b0ab10231
commit
c4d7711d2f
9 changed files with 141 additions and 151 deletions
|
@ -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()
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -118,7 +118,7 @@ internal class IncomingSASVerificationTransaction(
|
|||
}
|
||||
|
||||
//Bob’s device ensures that it has a copy of Alice’s 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 ")
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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")
|
||||
|
|
Loading…
Reference in a new issue