1
0
mirror of https://github.com/vector-im/riotX-android synced 2025-10-06 00:02:48 +02:00

Compare commits

...

4 Commits

Author SHA1 Message Date
Valere
3fa1560839 removed synchronized 2021-11-18 11:19:27 +01:00
Valere
1fc410a442 revert synchronized 2021-11-18 10:08:55 +01:00
Valere
3951499530 Code review 2021-11-18 10:06:36 +01:00
Valere
240fe6d43e Clean group key share + worker when failure + logs + synchronization
Update change logs


Cleaning


Bad try/catch wrapping


Ensure key share via worker in case of network failure
2021-11-18 09:59:34 +01:00
11 changed files with 251 additions and 93 deletions

2
changelog.d/4459.misc Normal file
View File

@@ -0,0 +1,2 @@
Improve group key sharing speed for huge groups
Improve e2e logging

View File

@@ -1315,7 +1315,7 @@ internal class DefaultCryptoService @Inject constructor(
}.fold(
{ callback.onSuccess(Unit) },
{
Timber.e("## CRYPTO | prepareToEncrypt() failed.")
Timber.e(it, "## CRYPTO | prepareToEncrypt() failed.")
callback.onFailure(it)
}
)

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2020 The Matrix.org Foundation C.I.C.
*
* 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 org.matrix.android.sdk.internal.crypto
import android.content.Context
import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass
import org.matrix.android.sdk.api.failure.shouldBeRetried
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.toDebugString
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.crypto.tasks.createUniqueTxnId
import org.matrix.android.sdk.internal.session.SessionComponent
import org.matrix.android.sdk.internal.worker.SessionSafeCoroutineWorker
import org.matrix.android.sdk.internal.worker.SessionWorkerParams
import timber.log.Timber
import javax.inject.Inject
internal class SimpleSendToDeviceWorker(context: Context,
params: WorkerParameters) :
SessionSafeCoroutineWorker<SimpleSendToDeviceWorker.Params>(context, params, Params::class.java) {
@JsonClass(generateAdapter = true)
internal data class Params(
override val sessionId: String,
val eventType: String,
val contentMap: Map<String /* userId */, Map<String /* deviceId */, Any>>,
val txnId: String? = null,
override val lastFailureMessage: String? = null
) : SessionWorkerParams
@Inject lateinit var sendToDeviceTask: SendToDeviceTask
override fun injectWith(injector: SessionComponent) {
injector.inject(this)
}
override suspend fun doSafeWork(params: Params): Result {
// params.txnId should be provided in all cases now. But Params can be deserialized by
// the WorkManager from data serialized in a previous version of the application, so without the txnId field.
// So if not present, we create a txnId
val txnId = params.txnId ?: createUniqueTxnId()
val eventType: String = params.eventType
val sendToDeviceMap = MXUsersDevicesMap<Any>().apply { addEntriesFromRawMap(params.contentMap) }
try {
Timber.d("## CRYPTO | Worker shareUserDevicesKey() $txnId: Sharing megolm session with ${sendToDeviceMap.toDebugString()} ")
sendToDeviceTask.execute(
SendToDeviceTask.Params(
eventType = eventType,
contentMap = sendToDeviceMap,
transactionId = txnId
)
)
Timber.d("## CRYPTO | Worker shareUserDevicesKey() $txnId ... success")
return Result.success()
} catch (throwable: Throwable) {
return if (throwable.shouldBeRetried()) {
Timber.d("## CRYPTO | Worker shareUserDevicesKey() $txnId ... schedule retry")
Result.retry()
} else {
Timber.d("## CRYPTO | Worker shareUserDevicesKey() $txnId ... failure")
buildErrorResult(params, throwable.localizedMessage ?: "error")
}
}
}
override fun buildErrorParams(params: Params, message: String): Params {
return params.copy(lastFailureMessage = params.lastFailureMessage ?: message)
}
}

View File

@@ -21,6 +21,8 @@ import org.matrix.android.sdk.internal.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.internal.crypto.model.MXKey
import org.matrix.android.sdk.internal.crypto.model.MXOlmSessionResult
import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.forEach
import org.matrix.android.sdk.internal.crypto.model.toDebugString
import org.matrix.android.sdk.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import timber.log.Timber
import javax.inject.Inject
@@ -33,29 +35,37 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
suspend fun handle(devicesByUser: Map<String, List<CryptoDeviceInfo>>, force: Boolean = false): MXUsersDevicesMap<MXOlmSessionResult> {
val devicesWithoutSession = ArrayList<CryptoDeviceInfo>()
val results = MXUsersDevicesMap<MXOlmSessionResult>()
val devicesWithSession = MXUsersDevicesMap<MXOlmSessionResult>()
for ((userId, deviceInfos) in devicesByUser) {
for (deviceInfo in deviceInfos) {
val deviceId = deviceInfo.deviceId
val key = deviceInfo.identityKey()
val sessionId = olmDevice.getSessionId(key!!)
if (sessionId.isNullOrEmpty() || force) {
devicesWithoutSession.add(deviceInfo)
if (key == null) {
Timber.w("## CRYPTO | Ignoring device (${deviceInfo.userId}|$deviceId) without identity key")
continue
}
val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId)
results.setObject(userId, deviceId, olmSessionResult)
val sessionId = olmDevice.getSessionId(key)
if (sessionId.isNullOrEmpty() || force) {
Timber.d("## CRYPTO | Found no existing olm session (${deviceInfo.userId}|$deviceId) (force=$force)")
devicesWithoutSession.add(deviceInfo)
} else {
Timber.d("## CRYPTO | using olm session $sessionId for (${deviceInfo.userId}|$deviceId)")
val olmSessionResult = MXOlmSessionResult(deviceInfo, sessionId)
devicesWithSession.setObject(userId, deviceId, olmSessionResult)
}
}
}
Timber.i("## CRYPTO | Devices without olm session (count:${devicesWithoutSession.size}) :" +
" ${devicesWithoutSession.joinToString { "${it.userId}|${it.deviceId}" }}")
if (devicesWithoutSession.size == 0) {
return results
return devicesWithSession
}
// Let's try to setup olm sessions with the other devices
// Prepare the request for claiming one-time keys
val usersDevicesToClaim = MXUsersDevicesMap<String>()
@@ -68,41 +78,36 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
// TODO: this has a race condition - if we try to send another message
// while we are claiming a key, we will end up claiming two and setting up
// two sessions.
//
// That should eventually resolve itself, but it's poor form.
Timber.i("## CRYPTO | claimOneTimeKeysForUsersDevices() : $usersDevicesToClaim")
Timber.i("## CRYPTO | claimOneTimeKeysForUsersDevices() : ${usersDevicesToClaim.toDebugString()}")
val claimParams = ClaimOneTimeKeysForUsersDeviceTask.Params(usersDevicesToClaim)
val oneTimeKeys = oneTimeKeysForUsersDeviceTask.executeRetry(claimParams, remainingRetry = ONE_TIME_KEYS_RETRY_COUNT)
Timber.v("## CRYPTO | claimOneTimeKeysForUsersDevices() : keysClaimResponse.oneTimeKeys: $oneTimeKeys")
for ((userId, deviceInfos) in devicesByUser) {
for (deviceInfo in deviceInfos) {
var oneTimeKey: MXKey? = null
val deviceIds = oneTimeKeys.getUserDeviceIds(userId)
if (null != deviceIds) {
for (deviceId in deviceIds) {
val olmSessionResult = results.getObject(userId, deviceId)
if (olmSessionResult!!.sessionId != null && !force) {
// We already have a result for this device
continue
}
val key = oneTimeKeys.getObject(userId, deviceId)
if (key?.type == oneTimeKeyAlgorithm) {
oneTimeKey = key
}
if (oneTimeKey == null) {
Timber.w("## CRYPTO | ensureOlmSessionsForDevices() : No one-time keys " + oneTimeKeyAlgorithm +
" for device " + userId + " : " + deviceId)
continue
}
// Update the result for this device in results
olmSessionResult.sessionId = verifyKeyAndStartSession(oneTimeKey, userId, deviceInfo)
// We iterate through claimed info to log missing otks
usersDevicesToClaim.forEach { userId, deviceId, _ ->
val foundOtk = oneTimeKeys.getObject(userId, deviceId)
if (foundOtk == null) {
Timber.d("## CRYPTO: No one time key for $userId|$deviceId")
} else if (foundOtk.type == oneTimeKeyAlgorithm) {
devicesByUser[userId]?.firstOrNull { it.deviceId == deviceId }?.let { cryptoDeviceInfo ->
Timber.d("## CRYPTO | creating outbound session for $userId|$deviceId ...")
val createdSession = verifyKeyAndStartSession(foundOtk, userId, cryptoDeviceInfo)
if (createdSession != null) {
Timber.d("## CRYPTO | ... created outbound session $createdSession for $userId|$deviceId")
devicesWithSession.setObject(userId, deviceId, MXOlmSessionResult(cryptoDeviceInfo, createdSession))
} else {
Timber.d("## CRYPTO | ... Failed to create outbound session for $userId|$deviceId")
}
}
} else {
// Skipping this key
Timber.i("## CRYPTO | skipping otk for $userId|$deviceId because unsupported algorithm")
}
}
return results
return devicesWithSession
}
private fun verifyKeyAndStartSession(oneTimeKey: MXKey, userId: String, deviceInfo: CryptoDeviceInfo): String? {
@@ -112,27 +117,34 @@ internal class EnsureOlmSessionsForDevicesAction @Inject constructor(
val signKeyId = "ed25519:$deviceId"
val signature = oneTimeKey.signatureForUserId(userId, signKeyId)
if (!signature.isNullOrEmpty() && !deviceInfo.fingerprint().isNullOrEmpty()) {
val fingerprint = deviceInfo.fingerprint()
if (!signature.isNullOrEmpty() && !fingerprint.isNullOrEmpty()) {
var isVerified = false
var errorMessage: String? = null
try {
olmDevice.verifySignature(deviceInfo.fingerprint()!!, oneTimeKey.signalableJSONDictionary(), signature)
olmDevice.verifySignature(fingerprint, oneTimeKey.signalableJSONDictionary(), signature)
isVerified = true
} catch (e: Exception) {
Timber.d(e, "## CRYPTO | verifyKeyAndStartSession() : Verify error for otk: ${oneTimeKey.signalableJSONDictionary()}," +
" signature:$signature fingerprint:$fingerprint")
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Verify error for ${deviceInfo.userId}|${deviceInfo.deviceId} " +
" - signable json ${oneTimeKey.signalableJSONDictionary()}")
errorMessage = e.message
}
// Check one-time key signature
if (isVerified) {
sessionId = olmDevice.createOutboundSession(deviceInfo.identityKey()!!, oneTimeKey.value)
sessionId = deviceInfo.identityKey()?.let { identityKey ->
olmDevice.createOutboundSession(identityKey, oneTimeKey.value)
}
if (!sessionId.isNullOrEmpty()) {
Timber.v("## CRYPTO | verifyKeyAndStartSession() : Started new sessionid " + sessionId +
" for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")")
} else {
if (sessionId.isNullOrEmpty()) {
// Possibly a bad key
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Error starting session with device $userId:$deviceId")
} else {
Timber.v("## CRYPTO | verifyKeyAndStartSession() : Started new sessionid " + sessionId +
" for device " + deviceInfo + "(theirOneTimeKey: " + oneTimeKey.value + ")")
}
} else {
Timber.e("## CRYPTO | verifyKeyAndStartSession() : Unable to verify signature on one-time key for device " + userId +

View File

@@ -335,7 +335,7 @@ internal class MXMegolmDecryption(private val userId: String,
runCatching { deviceListManager.downloadKeys(listOf(userId), false) }
.mapCatching {
val deviceId = request.deviceId
val deviceInfo = cryptoStore.getUserDevice(userId, deviceId ?: "")
val deviceInfo = it.getObject(userId, deviceId) // cryptoStore.getUserDevice(userId, deviceId ?: "")
if (deviceInfo == null) {
throw RuntimeException()
} else {

View File

@@ -16,8 +16,11 @@
package org.matrix.android.sdk.internal.crypto.algorithms.megolm
import androidx.work.BackoffPolicy
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.matrix.android.sdk.api.MatrixCoroutineDispatchers
import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.Content
@@ -26,6 +29,7 @@ import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.internal.crypto.DeviceListManager
import org.matrix.android.sdk.internal.crypto.MXCRYPTO_ALGORITHM_MEGOLM
import org.matrix.android.sdk.internal.crypto.MXOlmDevice
import org.matrix.android.sdk.internal.crypto.SimpleSendToDeviceWorker
import org.matrix.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import org.matrix.android.sdk.internal.crypto.actions.MessageEncrypter
import org.matrix.android.sdk.internal.crypto.algorithms.IMXEncrypting
@@ -36,28 +40,38 @@ import org.matrix.android.sdk.internal.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
import org.matrix.android.sdk.internal.crypto.model.event.WithHeldCode
import org.matrix.android.sdk.internal.crypto.model.forEach
import org.matrix.android.sdk.internal.crypto.model.toDebugCount
import org.matrix.android.sdk.internal.crypto.model.toDebugString
import org.matrix.android.sdk.internal.crypto.model.toShortDebugString
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.di.WorkManagerProvider
import org.matrix.android.sdk.internal.util.JsonCanonicalizer
import org.matrix.android.sdk.internal.util.convertToUTF8
import org.matrix.android.sdk.internal.worker.WorkerParamsFactory
import org.matrix.android.sdk.internal.worker.startChain
import timber.log.Timber
import java.util.UUID
import java.util.concurrent.TimeUnit
internal class MXMegolmEncryption(
// The id of the room we will be sending to.
private val roomId: String,
private val matrixSessionId: String,
private val olmDevice: MXOlmDevice,
private val defaultKeysBackupService: DefaultKeysBackupService,
private val cryptoStore: IMXCryptoStore,
private val deviceListManager: DeviceListManager,
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
private val userId: String,
private val deviceId: String,
private val myUserId: String,
private val myDeviceId: String,
private val sendToDeviceTask: SendToDeviceTask,
private val messageEncrypter: MessageEncrypter,
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val cryptoCoroutineScope: CoroutineScope
private val cryptoCoroutineScope: CoroutineScope,
private val workManagerProvider: WorkManagerProvider
) : IMXEncrypting, IMXGroupEncryption {
// OutboundSessionInfo. Null if we haven't yet started setting one up. Note
@@ -80,9 +94,8 @@ internal class MXMegolmEncryption(
eventType: String,
userIds: List<String>): Content {
val ts = System.currentTimeMillis()
Timber.v("## CRYPTO | encryptEventContent : getDevicesInRoom")
val devices = getDevicesInRoom(userIds)
Timber.v("## CRYPTO | encryptEventContent ${System.currentTimeMillis() - ts}: getDevicesInRoom ${devices.allowedDevices.map}")
Timber.d("## CRYPTO | encrypt event in room=$roomId - devices count in room ${devices.allowedDevices.toDebugCount()}")
val outboundSession = ensureOutboundSession(devices.allowedDevices)
return encryptContent(outboundSession, eventType, eventContent)
@@ -91,7 +104,7 @@ internal class MXMegolmEncryption(
// annoyingly we have to serialize again the saved outbound session to store message index :/
// if not we would see duplicate message index errors
olmDevice.storeOutboundGroupSessionForRoom(roomId, outboundSession.sessionId)
Timber.v("## CRYPTO | encryptEventContent: Finished in ${System.currentTimeMillis() - ts} millis")
Timber.v("## CRYPTO | encrypt event in room=$roomId Finished in ${System.currentTimeMillis() - ts} millis")
}
}
@@ -153,13 +166,14 @@ internal class MXMegolmEncryption(
* @param devicesInRoom the devices list
*/
private suspend fun ensureOutboundSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
Timber.v("## CRYPTO | ensureOutboundSession start")
Timber.v("## CRYPTO | ensureOutboundSession roomId:$roomId")
var session = outboundSession
if (session == null ||
// Need to make a brand new session?
session.needsRotation(sessionRotationPeriodMsgs, sessionRotationPeriodMs) ||
// Determine if we have shared with anyone we shouldn't have
session.sharedWithTooManyDevices(devicesInRoom)) {
Timber.d("## CRYPTO | roomId:$roomId Starting new megolm session because we need to rotate.")
session = prepareNewSessionInRoom()
outboundSession = session
}
@@ -176,6 +190,8 @@ internal class MXMegolmEncryption(
}
}
}
val devicesCount = shareMap.entries.fold(0) { acc, new -> acc + new.value.size }
Timber.d("## CRYPTO | roomId:$roomId found $devicesCount devices without megolm session(${session.sessionId})")
shareKey(safeSession, shareMap)
return safeSession
}
@@ -193,20 +209,15 @@ internal class MXMegolmEncryption(
Timber.v("## CRYPTO | shareKey() : nothing more to do")
return
}
// reduce the map size to avoid request timeout when there are too many devices (Users size * devices per user)
val subMap = HashMap<String, List<CryptoDeviceInfo>>()
var devicesCount = 0
for ((userId, devices) in devicesByUsers) {
subMap[userId] = devices
devicesCount += devices.size
if (devicesCount > 100) {
break
}
}
Timber.v("## CRYPTO | shareKey() ; sessionId<${session.sessionId}> userId ${subMap.keys}")
shareUserDevicesKey(session, subMap)
val remainingDevices = devicesByUsers - subMap.keys
shareKey(session, remainingDevices)
devicesByUsers
.flatMap { it.value }
.chunked(100)
.forEach { devices ->
Timber.d("## CRYPTO | share-megolm-key slice(${devices.size}) for room:$roomId session:${session.sessionId} " +
"for ${devices.joinToString { "${it.userId}|${it.deviceId}" }}")
shareUserDevicesKey(session, devices.groupBy { it.userId })
}
}
/**
@@ -241,25 +252,22 @@ internal class MXMegolmEncryption(
)
val contentMap = MXUsersDevicesMap<Any>()
var haveTargets = false
val userIds = results.userIds
val noOlmToNotify = mutableListOf<UserDevice>()
for (userId in userIds) {
val devicesToShareWith = devicesByUser[userId]
for ((deviceID) in devicesToShareWith!!) {
val sessionResult = results.getObject(userId, deviceID)
if (sessionResult?.sessionId == null) {
// no session with this device, probably because there
// were no one-time keys.
devicesByUser.forEach { entry ->
val userId = entry.key
entry.value.forEach { deviceInfo ->
val olmSession = results.getObject(userId, deviceInfo.deviceId)
if (olmSession?.sessionId == null) {
Timber.i("## CRYPTO | can't share megolm ${session.sessionId} with ${deviceInfo.toShortDebugString()}, no olm session")
// MSC 2399
// send withheld m.no_olm: an olm session could not be established.
// This may happen, for example, if the sender was unable to obtain a one-time key from the recipient.
noOlmToNotify.add(UserDevice(userId, deviceID))
continue
noOlmToNotify.add(UserDevice(userId, deviceInfo.deviceId))
} else {
contentMap.setObject(userId, deviceInfo.deviceId, messageEncrypter.encryptMessage(payload, listOf(deviceInfo)))
haveTargets = true
}
Timber.i("## CRYPTO | shareUserDevicesKey() : Add to share keys contentMap for $userId:$deviceID")
contentMap.setObject(userId, deviceID, messageEncrypter.encryptMessage(payload, listOf(sessionResult.deviceInfo)))
haveTargets = true
}
}
@@ -275,7 +283,7 @@ internal class MXMegolmEncryption(
gossipingEventBuffer.add(
Event(
type = EventType.ROOM_KEY,
senderId = this.userId,
senderId = this.myUserId,
content = submap.apply {
this["session_key"] = ""
// we add a fake key for trail
@@ -290,13 +298,36 @@ internal class MXMegolmEncryption(
if (haveTargets) {
t0 = System.currentTimeMillis()
Timber.i("## CRYPTO | shareUserDevicesKey() ${session.sessionId} : has target")
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
try {
sendToDeviceTask.execute(sendToDeviceParams)
Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
} catch (failure: Throwable) {
// What to do here...
Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ")
Timber.d("## CRYPTO | sending to device room key for ${session.sessionId} to ${contentMap.toDebugString()}")
withContext(Dispatchers.IO) {
val sendToDeviceParams = SendToDeviceTask.Params(EventType.ENCRYPTED, contentMap)
try {
sendToDeviceTask.execute(sendToDeviceParams)
Timber.i("## CRYPTO | shareUserDevicesKey() : sendToDevice succeeds after ${System.currentTimeMillis() - t0} ms")
} catch (failure: Throwable) {
Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session <${session.sessionId}> with $devicesByUser ")
Timber.e("## CRYPTO | shareUserDevicesKey() : Failed to share session: Scheduling worker for later delivery")
// As we mark those session as shared, to avoid retry for every next message, it could be nice here to create a worker
// fallback to ensure later delivery in case of any network issue?
workManagerProvider.matrixOneTimeWorkRequestBuilder<SimpleSendToDeviceWorker>()
.setConstraints(WorkManagerProvider.workConstraints)
.startChain(true)
.setInputData(
WorkerParamsFactory.toData(
SimpleSendToDeviceWorker.Params(
sessionId = matrixSessionId,
eventType = EventType.ENCRYPTED,
contentMap = contentMap.map,
txnId = UUID.randomUUID().toString()
)
)
)
.setBackoffCriteria(BackoffPolicy.LINEAR, WorkManagerProvider.BACKOFF_DELAY_MILLIS, TimeUnit.MILLISECONDS)
.build()
.let {
workManagerProvider.workManager.enqueue(it)
}
}
}
} else {
Timber.i("## CRYPTO | shareUserDevicesKey() : no need to sharekey")
@@ -317,7 +348,8 @@ internal class MXMegolmEncryption(
sessionId: String,
senderKey: String?,
code: WithHeldCode) {
Timber.i("## CRYPTO | notifyKeyWithHeld() :sending withheld key for $targets session:$sessionId and code $code")
Timber.d("## CRYPTO | notifyKeyWithHeld() :sending withheld for session:$sessionId and code $code to" +
" ${targets.joinToString { "${it.userId}|${it.deviceId}" }}")
val withHeldContent = RoomKeyWithHeldContent(
roomId = roomId,
senderKey = senderKey,
@@ -363,7 +395,7 @@ internal class MXMegolmEncryption(
// Include our device ID so that recipients can send us a
// m.new_device message if they don't have our session key.
map["device_id"] = deviceId
map["device_id"] = myDeviceId
session.useCount++
return map
}
@@ -447,7 +479,7 @@ internal class MXMegolmEncryption(
val usersDeviceMap = ensureOlmSessionsForDevicesAction.handle(devicesByUser)
val olmSessionResult = usersDeviceMap.getObject(userId, deviceId)
olmSessionResult?.sessionId // no session with this device, probably because there were no one-time keys.
// ensureOlmSessionsForDevicesAction has already done the logging, so just skip it.
// ensureOlmSessionsForDevicesAction has already done the logging, so just skip it.
?: return false.also {
Timber.w("## Crypto reshareKey: no session with this device, probably because there were no one-time keys")
}

View File

@@ -27,7 +27,9 @@ import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepo
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
import org.matrix.android.sdk.internal.crypto.tasks.SendToDeviceTask
import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.SessionId
import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.di.WorkManagerProvider
import javax.inject.Inject
internal class MXMegolmEncryptionFactory @Inject constructor(
@@ -38,27 +40,31 @@ internal class MXMegolmEncryptionFactory @Inject constructor(
private val ensureOlmSessionsForDevicesAction: EnsureOlmSessionsForDevicesAction,
@UserId private val userId: String,
@DeviceId private val deviceId: String?,
@SessionId private val sessionId: String,
private val sendToDeviceTask: SendToDeviceTask,
private val messageEncrypter: MessageEncrypter,
private val warnOnUnknownDevicesRepository: WarnOnUnknownDeviceRepository,
private val coroutineDispatchers: MatrixCoroutineDispatchers,
private val workManagerProvider: WorkManagerProvider,
private val cryptoCoroutineScope: CoroutineScope) {
fun create(roomId: String): MXMegolmEncryption {
return MXMegolmEncryption(
roomId = roomId,
matrixSessionId = sessionId,
olmDevice = olmDevice,
defaultKeysBackupService = defaultKeysBackupService,
cryptoStore = cryptoStore,
deviceListManager = deviceListManager,
ensureOlmSessionsForDevicesAction = ensureOlmSessionsForDevicesAction,
userId = userId,
deviceId = deviceId!!,
myUserId = userId,
myDeviceId = deviceId!!,
sendToDeviceTask = sendToDeviceTask,
messageEncrypter = messageEncrypter,
warnOnUnknownDevicesRepository = warnOnUnknownDevicesRepository,
coroutineDispatchers = coroutineDispatchers,
cryptoCoroutineScope = cryptoCoroutineScope
cryptoCoroutineScope = cryptoCoroutineScope,
workManagerProvider = workManagerProvider
)
}
}

View File

@@ -75,3 +75,5 @@ data class CryptoDeviceInfo(
internal fun CryptoDeviceInfo.toRest(): DeviceKeys {
return CryptoInfoMapper.map(this)
}
internal fun CryptoDeviceInfo.toShortDebugString() = "$userId|$deviceId"

View File

@@ -115,6 +115,12 @@ class MXUsersDevicesMap<E> {
}
}
fun addEntriesFromRawMap(other: Map<String /* userId */, Map<String /* deviceId */, E>>) {
other.forEach { (userID, deviceList) ->
setObjects(userID, deviceList)
}
}
override fun toString(): String {
return "MXUsersDevicesMap $map"
}
@@ -129,3 +135,11 @@ inline fun <T> MXUsersDevicesMap<T>.forEach(action: (String, String, T) -> Unit)
}
}
}
internal fun <T> MXUsersDevicesMap<T>.toDebugString() =
map.entries.joinToString { "${it.key} [${it.value.keys.joinToString { it }}]" }
internal fun <T> MXUsersDevicesMap<T>.toDebugCount() =
map.entries.fold(0) { acc, new ->
acc + new.value.keys.size
}

View File

@@ -25,6 +25,7 @@ import org.matrix.android.sdk.internal.crypto.CancelGossipRequestWorker
import org.matrix.android.sdk.internal.crypto.CryptoModule
import org.matrix.android.sdk.internal.crypto.SendGossipRequestWorker
import org.matrix.android.sdk.internal.crypto.SendGossipWorker
import org.matrix.android.sdk.internal.crypto.SimpleSendToDeviceWorker
import org.matrix.android.sdk.internal.crypto.crosssigning.UpdateTrustWorker
import org.matrix.android.sdk.internal.crypto.verification.SendVerificationMessageWorker
import org.matrix.android.sdk.internal.di.MatrixComponent
@@ -142,6 +143,8 @@ internal interface SessionComponent {
fun inject(worker: UpdateTrustWorker)
fun inject(worker: SimpleSendToDeviceWorker)
@Component.Factory
interface Factory {
fun create(

View File

@@ -46,6 +46,7 @@ internal class CryptoSyncHandler @Inject constructor(private val cryptoService:
event.getClearContent()?.toModel<MessageContent>()?.msgType == "m.bad.encrypted") {
Timber.e("## CRYPTO | handleToDeviceEvent() : Warning: Unable to decrypt to-device event : ${event.content}")
} else {
Timber.i("## CRYPTO | Decrypted to device event from ${event.senderId} of type:${event.type}")
verificationService.onToDeviceEvent(event)
cryptoService.onToDeviceEvent(event)
}