proper initial withheld support

This commit is contained in:
Valere 2022-04-12 23:37:04 +02:00
parent 88cf1a5e67
commit 54fb4ae8db
4 changed files with 69 additions and 7 deletions

View File

@ -35,6 +35,7 @@ import org.matrix.android.sdk.api.session.crypto.model.RoomKeyShareRequest
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent import org.matrix.android.sdk.api.session.events.model.content.EncryptedEventContent
import org.matrix.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.util.fromBase64 import org.matrix.android.sdk.api.util.fromBase64
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
@ -68,6 +69,7 @@ internal class OutgoingKeyRequestManager @Inject constructor(
private val cryptoConfig: MXCryptoConfig, private val cryptoConfig: MXCryptoConfig,
private val inboundGroupSessionStore: InboundGroupSessionStore, private val inboundGroupSessionStore: InboundGroupSessionStore,
private val sendToDeviceTask: DefaultSendToDeviceTask, private val sendToDeviceTask: DefaultSendToDeviceTask,
private val deviceListManager: DeviceListManager,
private val perSessionBackupQueryRateLimiter: PerSessionBackupQueryRateLimiter) { private val perSessionBackupQueryRateLimiter: PerSessionBackupQueryRateLimiter) {
private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher() private val dispatcher = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
@ -215,6 +217,41 @@ internal class OutgoingKeyRequestManager @Inject constructor(
outgoingRequestScope.launch { outgoingRequestScope.launch {
sequencer.post { sequencer.post {
Timber.tag(loggerTag.value).d("Withheld received for $sessionId from ${event.senderId}|$fromDevice") Timber.tag(loggerTag.value).d("Withheld received for $sessionId from ${event.senderId}|$fromDevice")
Timber.tag(loggerTag.value).v("Withheld content ${event.getClearContent()}")
// We want to store withheld code from the sender of the message (owner of the megolm session), not from
// other devices that might gossip the key. If not the initial reason might be overridden
// by a request to one of our session.
event.getClearContent().toModel<RoomKeyWithHeldContent>()?.let { withheld ->
withContext(coroutineDispatchers.crypto) {
tryOrNull {
deviceListManager.downloadKeys(listOf(event.senderId ?: ""), false)
}
cryptoStore.getUserDeviceList(event.senderId ?: "")
.also { devices ->
Timber.tag(loggerTag.value).v("Withheld Devices for ${event.senderId} are ${devices.orEmpty().joinToString { it.identityKey() ?: "" }}")
}
?.firstOrNull {
it.identityKey() == senderKey
}
}.also {
Timber.tag(loggerTag.value).v("Withheld device for sender key $senderKey is from ${it?.shortDebugString()}")
}?.let {
if (it.userId == event.senderId) {
if (fromDevice != null) {
if (it.deviceId == fromDevice) {
Timber.tag(loggerTag.value).v("Storing sender Withheld code ${withheld.code} for ${withheld.sessionId}")
cryptoStore.addWithHeldMegolmSession(withheld)
}
} else {
Timber.tag(loggerTag.value).v("Storing sender Withheld code ${withheld.code} for ${withheld.sessionId}")
cryptoStore.addWithHeldMegolmSession(withheld)
}
}
}
}
// Here we store the replies from a given request
cryptoStore.updateOutgoingRoomKeyReply( cryptoStore.updateOutgoingRoomKeyReply(
roomId = roomId, roomId = roomId,
sessionId = sessionId, sessionId = sessionId,

View File

@ -102,9 +102,20 @@ internal class MXMegolmDecryption(
if (throwable is MXCryptoError.OlmError) { if (throwable is MXCryptoError.OlmError) {
// TODO Check the value of .message // TODO Check the value of .message
if (throwable.olmException.message == "UNKNOWN_MESSAGE_INDEX") { if (throwable.olmException.message == "UNKNOWN_MESSAGE_INDEX") {
// So we know that session, but it's ratcheted and we can't decrypt at that index
if (requestKeysOnFail) { if (requestKeysOnFail) {
requestKeysForEvent(event) requestKeysForEvent(event)
} }
// Check if partially withheld
val withHeldInfo = cryptoStore.getWithHeldMegolmSession(event.roomId, encryptedEventContent.sessionId)
if (withHeldInfo != null) {
// Encapsulate as withHeld exception
throw MXCryptoError.Base(MXCryptoError.ErrorType.KEYS_WITHHELD,
withHeldInfo.code?.value ?: "",
withHeldInfo.reason)
}
throw MXCryptoError.Base( throw MXCryptoError.Base(
MXCryptoError.ErrorType.UNKNOWN_MESSAGE_INDEX, MXCryptoError.ErrorType.UNKNOWN_MESSAGE_INDEX,
"UNKNOWN_MESSAGE_INDEX", "UNKNOWN_MESSAGE_INDEX",
@ -121,6 +132,18 @@ internal class MXMegolmDecryption(
} }
if (throwable is MXCryptoError.Base) { if (throwable is MXCryptoError.Base) {
if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) { if (throwable.errorType == MXCryptoError.ErrorType.UNKNOWN_INBOUND_SESSION_ID) {
// Check if it was withheld by sender to enrich error code
val withHeldInfo = cryptoStore.getWithHeldMegolmSession(event.roomId, encryptedEventContent.sessionId)
if (withHeldInfo != null) {
if (requestKeysOnFail) {
requestKeysForEvent(event)
}
// Encapsulate as withHeld exception
throw MXCryptoError.Base(MXCryptoError.ErrorType.KEYS_WITHHELD,
withHeldInfo.code?.value ?: "",
withHeldInfo.reason)
}
if (requestKeysOnFail) { if (requestKeysOnFail) {
requestKeysForEvent(event) requestKeysForEvent(event)
} }

View File

@ -328,7 +328,8 @@ internal class MXMegolmEncryption(
senderKey = senderKey, senderKey = senderKey,
algorithm = MXCRYPTO_ALGORITHM_MEGOLM, algorithm = MXCRYPTO_ALGORITHM_MEGOLM,
sessionId = sessionId, sessionId = sessionId,
codeString = code.value codeString = code.value,
fromDevice = myDeviceId
) )
val params = SendToDeviceTask.Params( val params = SendToDeviceTask.Params(
EventType.ROOM_KEY_WITHHELD, EventType.ROOM_KEY_WITHHELD,

View File

@ -271,12 +271,13 @@ internal class RealmCryptoStore @Inject constructor(
} }
override fun deviceWithIdentityKey(identityKey: String): CryptoDeviceInfo? { override fun deviceWithIdentityKey(identityKey: String): CryptoDeviceInfo? {
return doWithRealm(realmConfiguration) { return doWithRealm(realmConfiguration) { realm ->
it.where<DeviceInfoEntity>() realm.where<DeviceInfoEntity>()
.equalTo(DeviceInfoEntityFields.IDENTITY_KEY, identityKey) .contains(DeviceInfoEntityFields.KEYS_MAP_JSON, identityKey)
.findFirst() .findAll()
?.let { deviceInfo -> .mapNotNull { CryptoMapper.mapToModel(it) }
CryptoMapper.mapToModel(deviceInfo) .firstOrNull {
it.identityKey() == identityKey
} }
} }
} }