Skip to content

Commit 1035bee

Browse files
committed
crypto: share key from ratchet encryption
1 parent f4513d2 commit 1035bee

File tree

7 files changed

+104
-62
lines changed

7 files changed

+104
-62
lines changed

sdn-sdk-android/src/main/java/org/sdn/android/sdk/internal/crypto/DefaultCryptoService.kt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,7 +867,10 @@ internal class DefaultCryptoService @Inject constructor(
867867
// very silly, it won't work because an Olm session cannot send
868868
// messages to itself.
869869
if (req.requestingDeviceId != deviceId) { // ignore self requests
870-
event.senderId?.let { incomingKeyRequestManager.addNewIncomingRequest(it, req) }
870+
event.senderId?.let {
871+
ensureRequestingUserDevice(it, req.requestingDeviceId)
872+
incomingKeyRequestManager.addNewIncomingRequest(it, req)
873+
}
871874
}
872875
}
873876
}
@@ -885,6 +888,20 @@ internal class DefaultCryptoService @Inject constructor(
885888
liveEventManager.get().dispatchOnLiveToDevice(event)
886889
}
887890

891+
private suspend fun ensureRequestingUserDevice(userId: String?, deviceId: String?) : CryptoDeviceInfo? {
892+
if (userId == null || deviceId == null) {
893+
return null
894+
}
895+
var requestingDevice = cryptoStore.getUserDevice(userId, deviceId)
896+
if (requestingDevice == null) {
897+
Timber.tag(loggerTag.value).w("ensureRequestingUserDevice() : Unknown device: $userId | $deviceId")
898+
899+
val deviceMap = deviceListManager.downloadKeys(listOf(userId), true)
900+
requestingDevice = deviceMap.getObject(userId, deviceId)
901+
}
902+
return requestingDevice
903+
}
904+
888905
/**
889906
* Handle a key event.
890907
*

sdn-sdk-android/src/main/java/org/sdn/android/sdk/internal/crypto/DeviceListManager.kt

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -211,21 +211,6 @@ internal class DeviceListManager @Inject constructor(
211211
if (isUpdated) {
212212
cryptoStore.saveDeviceTrackingStatuses(deviceTrackingStatuses)
213213
}
214-
215-
// removeCurrentGroupSession on device changed
216-
val sharedRoomIds = ArrayList<String>()
217-
for (roomId in cryptoStore.getAllCryptoRooms()) {
218-
for (userId in cryptoSessionInfoProvider.getRoomUserIds(roomId, true)) {
219-
if (changed.contains(userId) || left.contains((userId))) {
220-
sharedRoomIds.add(roomId)
221-
}
222-
}
223-
}
224-
if (sharedRoomIds.isNotEmpty()) {
225-
Timber.v("## CRYPTO: removeCurrentGroupSession on device changed: $sharedRoomIds")
226-
cryptoStore.removeAllCurrentGroupSession(sharedRoomIds.toTypedArray())
227-
}
228-
229214
}
230215

231216
/**

sdn-sdk-android/src/main/java/org/sdn/android/sdk/internal/crypto/IncomingKeyRequestManager.kt

Lines changed: 6 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -225,19 +225,10 @@ internal class IncomingKeyRequestManager @Inject constructor(
225225
request.requestingDeviceId
226226
)
227227

228-
val roomAlgorithm = // withContext(coroutineDispatchers.crypto) {
229-
cryptoStore.getRoomAlgorithm(request.roomId)
230-
// }
231-
if (!arrayOf(MXCRYPTO_ALGORITHM_MEGOLM, MXCRYPTO_ALGORITHM_RATCHET).contains(roomAlgorithm)) {
228+
if (!arrayOf(MXCRYPTO_ALGORITHM_MEGOLM, MXCRYPTO_ALGORITHM_RATCHET).contains(request.algorithm)) {
232229
// strange we received a request for a room that is not encrypted
233230
// maybe a broken state?
234-
Timber.tag(loggerTag.value).w("Received a key request in a room with unsupported alg:$roomAlgorithm , req:${request.shortDbgString()}")
235-
return
236-
}
237-
238-
// share key if algorithm is MXCRYPTO_ALGORITHM_RATCHET
239-
if (roomAlgorithm == MXCRYPTO_ALGORITHM_RATCHET) {
240-
shareMegolmKey(request, requestingDevice, null)
231+
Timber.tag(loggerTag.value).w("Received a key request in a room with unsupported alg:${request.algorithm} , req:${request.shortDbgString()}")
241232
return
242233
}
243234

@@ -255,7 +246,8 @@ internal class IncomingKeyRequestManager @Inject constructor(
255246
// we share from the earliest known chain index
256247
shareMegolmKey(request, requestingDevice, null)
257248
} else {
258-
shareIfItWasPreviouslyShared(request, requestingDevice)
249+
Timber.tag(loggerTag.value).w("requesting device is not verified, still share the key")
250+
shareMegolmKey(request, requestingDevice, null)
259251
}
260252
} else {
261253
if (cryptoConfig.limitRoomKeyRequestsToMyDevices) {
@@ -265,9 +257,10 @@ internal class IncomingKeyRequestManager @Inject constructor(
265257
Timber.tag(loggerTag.value).v("handling request from other user: megolm session ${request.sessionId}")
266258
if (requestingDevice.isBlocked) {
267259
// it's blocked, so send a withheld code
260+
Timber.tag(loggerTag.value).w("requesting device is blocked, will not share the key")
268261
sendWithheldForRequest(request, WithHeldCode.BLACKLISTED)
269262
} else {
270-
shareIfItWasPreviouslyShared(request, requestingDevice)
263+
shareMegolmKey(request, requestingDevice, null)
271264
}
272265
}
273266
}

sdn-sdk-android/src/main/java/org/sdn/android/sdk/internal/crypto/MXOlmDevice.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ internal class MXOlmDevice @Inject constructor(
517517

518518
return MXOutboundSessionInfo(
519519
sessionId = sessionId,
520+
senderKey = this.deviceCurve25519Key,
520521
sharedWithHelper = SharedWithHelper(roomId, sessionId, store),
521522
clock = clock,
522523
creationTime = restoredOutboundGroupSession.creationTime,

sdn-sdk-android/src/main/java/org/sdn/android/sdk/internal/crypto/algorithms/megolm/MXMegolmEncryption.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,7 @@ internal class MXMegolmEncryption(
189189

190190
return MXOutboundSessionInfo(
191191
sessionId = sessionId,
192+
senderKey = olmDevice.deviceCurve25519Key,
192193
sharedWithHelper = SharedWithHelper(roomId, sessionId, cryptoStore),
193194
clock = clock,
194195
sharedHistory = sharedHistory

sdn-sdk-android/src/main/java/org/sdn/android/sdk/internal/crypto/algorithms/megolm/MXOutboundSessionInfo.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import timber.log.Timber
2424
internal class MXOutboundSessionInfo(
2525
// The id of the session
2626
val sessionId: String,
27+
val senderKey: String?,
2728
val sharedWithHelper: SharedWithHelper,
2829
private val clock: Clock,
2930
// When the session was created

sdn-sdk-android/src/main/java/org/sdn/android/sdk/internal/crypto/algorithms/megolm/MXRatchetEncryption.kt

Lines changed: 77 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import org.sdn.android.sdk.api.logger.LoggerTag
2626
import org.sdn.android.sdk.api.session.crypto.MXCryptoError
2727
import org.sdn.android.sdk.api.session.crypto.model.CryptoDeviceInfo
2828
import org.sdn.android.sdk.api.session.crypto.model.MXUsersDevicesMap
29-
import org.sdn.android.sdk.api.session.crypto.model.UnknownInfo.sessionId
3029
import org.sdn.android.sdk.api.session.crypto.model.forEach
3130
import org.sdn.android.sdk.api.session.events.model.Content
3231
import org.sdn.android.sdk.api.session.events.model.EventType
@@ -37,6 +36,7 @@ import org.sdn.android.sdk.api.session.room.model.message.MessageType
3736
import org.sdn.android.sdk.internal.crypto.DeviceListManager
3837
import org.sdn.android.sdk.internal.crypto.InboundGroupSessionHolder
3938
import org.sdn.android.sdk.internal.crypto.MXOlmDevice
39+
import org.sdn.android.sdk.internal.crypto.MegolmSessionData
4040
import org.sdn.android.sdk.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
4141
import org.sdn.android.sdk.internal.crypto.actions.MessageEncrypter
4242
import org.sdn.android.sdk.internal.crypto.algorithms.IMXEncrypting
@@ -76,11 +76,11 @@ internal class MXRatchetEncryption(
7676
// OutboundSessionInfo. Null if we haven't yet started setting one up. Note
7777
// that even if this is non-null, it may not be ready for use (in which
7878
// case outboundSession.shareOperation will be non-null.)
79-
private var currentSession: MXOutboundSessionInfo? = null
79+
private var outboundSession: MXOutboundSessionInfo? = null
8080

8181
init {
8282
// restore existing outbound session if any
83-
currentSession = olmDevice.restoreOutboundGroupSessionForRoom(roomId)
83+
outboundSession = olmDevice.restoreOutboundGroupSessionForRoom(roomId)
8484
}
8585

8686
override suspend fun encryptEventContent(
@@ -138,7 +138,7 @@ internal class MXRatchetEncryption(
138138
}
139139

140140
override fun discardSessionKey() {
141-
currentSession = null
141+
outboundSession = null
142142
olmDevice.discardOutboundGroupSessionForRoom(roomId)
143143
}
144144

@@ -185,6 +185,7 @@ internal class MXRatchetEncryption(
185185

186186
return MXOutboundSessionInfo(
187187
sessionId = sessionId,
188+
senderKey = olmDevice.deviceCurve25519Key!!,
188189
sharedWithHelper = SharedWithHelper(roomId, sessionId, cryptoStore),
189190
clock = clock,
190191
sharedHistory = sharedHistory
@@ -198,28 +199,50 @@ internal class MXRatchetEncryption(
198199
*/
199200
private suspend fun ensureCurrentSession(devicesInRoom: MXUsersDevicesMap<CryptoDeviceInfo>): MXOutboundSessionInfo {
200201
Timber.tag(loggerTag.value).v("ensureCurrentSession roomId:$roomId")
202+
val safeSession: MXOutboundSessionInfo
203+
val sharedHistory = cryptoStore.shouldShareHistory(roomId)
201204
val currentGroupSession = olmDevice.getCurrentGroupSession(roomId)
202205
val sessionId = currentGroupSession?.wrapper?.safeSessionId
203-
val safeSession: MXOutboundSessionInfo
204-
var needShare = true
205-
if (sessionId != null) {
206-
val sharedHistory = cryptoStore.shouldShareHistory(roomId)
206+
val isNewSession: Boolean
207+
val exportedSession: MegolmSessionData?
208+
209+
if (currentGroupSession != null && sessionId != null) {
210+
Timber.tag(loggerTag.value).i("ensureCurrentSession() : reuse existing session $sessionId in $roomId")
207211
safeSession = MXOutboundSessionInfo(
208212
sessionId = sessionId,
213+
senderKey = currentGroupSession.wrapper.senderKey,
209214
sharedWithHelper = SharedWithHelper(roomId, sessionId, cryptoStore),
210215
clock = clock,
211216
sharedHistory = sharedHistory
212217
)
213-
// if the session is not created by us then no need to share.
214-
if (olmDevice.getSessionKey(sessionId) == null) {
215-
needShare = false
218+
isNewSession = false
219+
exportedSession = currentGroupSession.mutex.withLock {
220+
currentGroupSession.wrapper.exportKeys()
216221
}
217-
} else {
218-
safeSession = prepareNewSessionInRoom()
219-
}
220222

221-
if (!needShare) {
222-
return safeSession
223+
} else {
224+
var session = outboundSession
225+
if (session == null) {
226+
session = prepareNewSessionInRoom()
227+
outboundSession = session
228+
} else {
229+
olmDevice.addInboundGroupSession(
230+
sessionId = session.sessionId,
231+
sessionKey = olmDevice.getSessionKey(session.sessionId)!!,
232+
roomId = roomId,
233+
algorithm = MXCRYPTO_ALGORITHM_RATCHET,
234+
senderKey = olmDevice.deviceCurve25519Key!!,
235+
forwardingCurve25519KeyChain = emptyList(),
236+
keysClaimed = mapOf("ed25519" to olmDevice.deviceEd25519Key!!),
237+
exportFormat = false,
238+
sharedHistory = sharedHistory,
239+
trusted = true
240+
)
241+
}
242+
safeSession = session
243+
isNewSession = true
244+
exportedSession = null
245+
Timber.tag(loggerTag.value).i("ensureCurrentSession() : use new session ${session.sessionId} in $roomId")
223246
}
224247

225248
val shareMap = HashMap<String, MutableList<CryptoDeviceInfo>>()/* userId */
@@ -236,7 +259,18 @@ internal class MXRatchetEncryption(
236259
}
237260
val devicesCount = shareMap.entries.fold(0) { acc, new -> acc + new.value.size }
238261
Timber.tag(loggerTag.value).d("roomId:$roomId found $devicesCount devices without megolm session(${safeSession.sessionId})")
239-
shareKey(safeSession, shareMap)
262+
263+
// measure time consumed
264+
val ts = clock.epochMillis()
265+
if (isNewSession) {
266+
shareKey(safeSession, null, shareMap)
267+
} else {
268+
// offload to io thread
269+
cryptoCoroutineScope.launch(coroutineDispatchers.io) {
270+
shareKey(safeSession, exportedSession, shareMap)
271+
}
272+
}
273+
Timber.tag(loggerTag.value).d("shareKey in $roomId done in ${clock.epochMillis() - ts} millis")
240274
return safeSession
241275
}
242276

@@ -248,6 +282,7 @@ internal class MXRatchetEncryption(
248282
*/
249283
private suspend fun shareKey(
250284
session: MXOutboundSessionInfo,
285+
export: MegolmSessionData?,
251286
devicesByUsers: Map<String, List<CryptoDeviceInfo>>
252287
) {
253288
// nothing to send, the task is done
@@ -265,10 +300,11 @@ internal class MXRatchetEncryption(
265300
break
266301
}
267302
}
303+
268304
Timber.tag(loggerTag.value).v("shareKey() ; sessionId<${session.sessionId}> userId ${subMap.keys}")
269-
shareUserDevicesKey(session, subMap)
305+
shareUserDevicesKey(session, export, subMap)
270306
val remainingDevices = devicesByUsers - subMap.keys
271-
shareKey(session, remainingDevices)
307+
shareKey(session, export, remainingDevices)
272308
}
273309

274310
/**
@@ -279,24 +315,32 @@ internal class MXRatchetEncryption(
279315
*/
280316
private suspend fun shareUserDevicesKey(
281317
sessionInfo: MXOutboundSessionInfo,
318+
exportedSessionData: MegolmSessionData?,
282319
devicesByUser: Map<String, List<CryptoDeviceInfo>>
283320
) {
284-
val sessionKey = olmDevice.getSessionKey(sessionInfo.sessionId) ?: return Unit.also {
285-
Timber.tag(loggerTag.value).v("shareUserDevicesKey() Failed to share session, failed to export")
286-
}
287321
val chainIndex = olmDevice.getMessageIndex(sessionInfo.sessionId)
288322

289-
val payload = mapOf(
290-
"type" to EventType.ROOM_KEY,
291-
"content" to mapOf(
292-
"algorithm" to MXCRYPTO_ALGORITHM_RATCHET,
293-
"room_id" to roomId,
294-
"session_id" to sessionInfo.sessionId,
295-
"session_key" to sessionKey,
296-
"chain_index" to chainIndex,
297-
"org.matrix.msc3061.shared_history" to sessionInfo.sharedHistory
323+
val payload = if (exportedSessionData != null) {
324+
mapOf(
325+
"type" to EventType.FORWARDED_ROOM_KEY,
326+
"content" to exportedSessionData
298327
)
299-
)
328+
} else {
329+
val sessionKey = olmDevice.getSessionKey(sessionInfo.sessionId) ?: return Unit.also {
330+
Timber.tag(loggerTag.value).e("shareUserDevicesKey() Failed to share session, failed to export")
331+
}
332+
mapOf(
333+
"type" to EventType.ROOM_KEY,
334+
"content" to mapOf(
335+
"algorithm" to MXCRYPTO_ALGORITHM_RATCHET,
336+
"room_id" to roomId,
337+
"session_id" to sessionInfo.sessionId,
338+
"session_key" to sessionKey,
339+
"chain_index" to chainIndex,
340+
"org.matrix.msc3061.shared_history" to sessionInfo.sharedHistory
341+
)
342+
)
343+
}
300344

301345
var t0 = clock.epochMillis()
302346
Timber.tag(loggerTag.value).v("shareUserDevicesKey() : starts")
@@ -421,7 +465,7 @@ internal class MXRatchetEncryption(
421465
payloadJson["type"] = eventType
422466
payloadJson["content"] = eventContent
423467

424-
val sessionWrapper = olmDevice.getCurrentGroupSession(roomId)?.wrapper ?: throw Exception("no current session for room: $roomId")
468+
val sessionWrapper = olmDevice.getInboundGroupSession(session.sessionId, session.senderKey, roomId).wrapper
425469
val currentSession = sessionWrapper.session
426470
if (currentSession.sessionIdentifier() != session.sessionId) {
427471
throw Exception("current session $session.sessionId is invalid for room: $roomId")

0 commit comments

Comments
 (0)