16
16
17
17
package org.sdn.android.sdk.internal.crypto
18
18
19
+ import dagger.Lazy
19
20
import kotlinx.coroutines.CoroutineScope
20
21
import kotlinx.coroutines.SupervisorJob
21
22
import kotlinx.coroutines.asCoroutineDispatcher
@@ -30,6 +31,7 @@ import org.sdn.android.sdk.api.crypto.MXCRYPTO_ALGORITHM_RATCHET
30
31
import org.sdn.android.sdk.api.crypto.MXCryptoConfig
31
32
import org.sdn.android.sdk.api.extensions.tryOrNull
32
33
import org.sdn.android.sdk.api.logger.LoggerTag
34
+ import org.sdn.android.sdk.api.session.crypto.OutgoingRoomKeyRequestState
33
35
import org.sdn.android.sdk.api.session.crypto.keyshare.GossipingRequestListener
34
36
import org.sdn.android.sdk.api.session.crypto.model.CryptoDeviceInfo
35
37
import org.sdn.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest
@@ -39,9 +41,11 @@ import org.sdn.android.sdk.api.session.crypto.model.RoomKeyShareRequest
39
41
import org.sdn.android.sdk.api.session.events.model.EventType
40
42
import org.sdn.android.sdk.api.session.events.model.content.RoomKeyWithHeldContent
41
43
import org.sdn.android.sdk.api.session.events.model.content.WithHeldCode
44
+ import org.sdn.android.sdk.api.session.events.model.toContent
42
45
import org.sdn.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
43
46
import org.sdn.android.sdk.internal.crypto.actions.MessageEncrypter
44
47
import org.sdn.android.sdk.internal.crypto.store.IMXCryptoStore
48
+ import org.sdn.android.sdk.internal.crypto.tasks.SendSimpleEventTask
45
49
import org.sdn.android.sdk.internal.crypto.tasks.SendToDeviceTask
46
50
import org.sdn.android.sdk.internal.session.SessionScope
47
51
import org.sdn.android.sdk.internal.task.SemaphoreCoroutineSequencer
@@ -63,6 +67,7 @@ internal class IncomingKeyRequestManager @Inject constructor(
63
67
private val messageEncrypter : MessageEncrypter ,
64
68
private val coroutineDispatchers : SDNCoroutineDispatchers ,
65
69
private val sendToDeviceTask : SendToDeviceTask ,
70
+ private val sendSimpleEventTask : SendSimpleEventTask ,
66
71
private val clock : Clock ,
67
72
) {
68
73
@@ -83,6 +88,8 @@ internal class IncomingKeyRequestManager @Inject constructor(
83
88
val requestId : String ,
84
89
val requestingUserId : String ,
85
90
val requestingDeviceId : String ,
91
+ val requestingDeviceKey : String? ,
92
+ val requestingDeviceOtk : String? ,
86
93
val roomId : String ,
87
94
val senderKey : String ,
88
95
val sessionId : String ,
@@ -109,6 +116,8 @@ internal class IncomingKeyRequestManager @Inject constructor(
109
116
requestId = requestId,
110
117
requestingUserId = senderId,
111
118
requestingDeviceId = deviceId,
119
+ requestingDeviceKey = this .requestingDeviceKey,
120
+ requestingDeviceOtk = this .requestingDeviceOtk,
112
121
roomId = roomId,
113
122
senderKey = senderKey,
114
123
sessionId = sessionId,
@@ -209,11 +218,11 @@ internal class IncomingKeyRequestManager @Inject constructor(
209
218
210
219
private suspend fun handleIncomingRequest (request : ValidMegolmRequestBody ) {
211
220
// We don't want to download keys, if we don't know the device yet we won't share any how?
212
- val requestingDevice =
213
- cryptoStore.getUserDevice(request.requestingUserId, request.requestingDeviceId)
214
- ? : return Unit . also {
215
- Timber .tag(loggerTag.value).d( " Ignoring key request: ${request.shortDbgString()} " )
216
- }
221
+ val requestingDevice = cryptoStore.getUserDevice(request.requestingUserId, request.requestingDeviceId)
222
+ if (requestingDevice == null || requestingDevice.identityKey() != request.requestingDeviceKey) {
223
+ directShareMegolmKey(request)
224
+ return
225
+ }
217
226
218
227
cryptoStore.saveIncomingKeyRequestAuditTrail(
219
228
request.requestId,
@@ -357,6 +366,8 @@ internal class IncomingKeyRequestManager @Inject constructor(
357
366
requestId = request.requestId,
358
367
requestingDeviceId = request.deviceId,
359
368
requestingUserId = request.userId,
369
+ requestingDeviceKey = null ,
370
+ requestingDeviceOtk = null ,
360
371
roomId = request.requestBody.roomId,
361
372
senderKey = request.requestBody.senderKey,
362
373
sessionId = request.requestBody.sessionId,
@@ -446,6 +457,85 @@ internal class IncomingKeyRequestManager @Inject constructor(
446
457
}
447
458
}
448
459
460
+ private suspend fun directShareMegolmKey (validRequest : ValidMegolmRequestBody ): Boolean {
461
+ val requestingDeviceKey = validRequest.requestingDeviceKey ? : return false .also {
462
+ Timber .tag(loggerTag.value).e(" directShareMegolmKey: no requestingDeviceKey from ${validRequest.requestingUserId} | ${validRequest.requestingDeviceId} " )
463
+ }
464
+ val requestingDeviceInfo = CryptoDeviceInfo (
465
+ userId = validRequest.requestingUserId,
466
+ deviceId = validRequest.requestingDeviceId,
467
+ keys = mapOf (" curve25519:${validRequest.requestingDeviceId} " to requestingDeviceKey)
468
+ )
469
+ val wasSessionSharedWithUser = cryptoStore.getSharedSessionInfo(validRequest.roomId, validRequest.sessionId, requestingDeviceInfo)
470
+ if (wasSessionSharedWithUser.found && wasSessionSharedWithUser.chainIndex == 0 ) {
471
+ Timber .tag(loggerTag.value).e(" skip direct share Key for ${validRequest.shortDbgString()} " )
472
+ return false
473
+ }
474
+
475
+ Timber .tag(loggerTag.value).d(" try to direct share Megolm Key for ${validRequest.shortDbgString()} " )
476
+ if (validRequest.requestingDeviceKey == null || validRequest.requestingDeviceOtk == null ) {
477
+ Timber .tag(loggerTag.value).w(" fail to direct share Megolm Key for ${validRequest.shortDbgString()} : no device keys" )
478
+ return false
479
+ }
480
+
481
+ val sessionHolder = try {
482
+ olmDevice.getInboundGroupSession(validRequest.sessionId, validRequest.senderKey, validRequest.roomId)
483
+ } catch (failure: Throwable ) {
484
+ Timber .tag(loggerTag.value)
485
+ .e(failure, " shareKeysWithDevice: failed to get session ${validRequest.requestingUserId} " )
486
+ // It's unavailable
487
+ sendWithheldForRequest(validRequest, WithHeldCode .UNAVAILABLE )
488
+ return false
489
+ }
490
+
491
+ val export = sessionHolder.mutex.withLock {
492
+ sessionHolder.wrapper.exportKeys(0 )
493
+ } ? : return false .also {
494
+ Timber .tag(loggerTag.value)
495
+ .e(" shareKeysWithDevice: failed to export group session ${validRequest.sessionId} " )
496
+ }
497
+
498
+ val payloadJson = mapOf (
499
+ " type" to EventType .FORWARDED_ROOM_KEY ,
500
+ " content" to export
501
+ )
502
+
503
+ val encryptedPayload = messageEncrypter.directEncryptMessage(
504
+ payloadJson, validRequest.requestingUserId, validRequest.requestingDeviceId, validRequest.requestingDeviceKey, validRequest.requestingDeviceOtk)
505
+ encryptedPayload.traceId = validRequest.sessionId
506
+
507
+ return try {
508
+ sendSimpleEventTask.execute(SendSimpleEventTask .Params (
509
+ roomId = validRequest.roomId,
510
+ eventType = EventType .ROOM_KEY_REPLY ,
511
+ encryptedPayload.toContent()
512
+ ))
513
+ Timber .tag(loggerTag.value)
514
+ .i(" successfully direct shared session for ${validRequest.shortDbgString()} " )
515
+ cryptoStore.saveForwardKeyAuditTrail(
516
+ validRequest.roomId,
517
+ validRequest.sessionId,
518
+ validRequest.senderKey,
519
+ validRequest.algorithm,
520
+ validRequest.requestingUserId,
521
+ validRequest.requestingDeviceId,
522
+ 0 ,
523
+ )
524
+ cryptoStore.markedSessionAsShared(
525
+ validRequest.roomId,
526
+ validRequest.sessionId,
527
+ validRequest.requestingUserId,
528
+ validRequest.requestingDeviceId,
529
+ validRequest.requestingDeviceKey,
530
+ 0 )
531
+ true
532
+ } catch (failure: Throwable ) {
533
+ Timber .tag(loggerTag.value)
534
+ .e(failure, " fail to direct share session for ${validRequest.shortDbgString()} " )
535
+ false
536
+ }
537
+ }
538
+
449
539
fun addRoomKeysRequestListener (listener : GossipingRequestListener ) {
450
540
synchronized(gossipingRequestListeners) {
451
541
gossipingRequestListeners.add(listener)
0 commit comments