1
1
/*
2
- Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
2
+ Copyright 2015 - 2021, 2023 The Matrix.org Foundation C.I.C.
3
3
4
4
Licensed under the Apache License, Version 2.0 (the "License");
5
5
you may not use this file except in compliance with the License.
@@ -43,6 +43,7 @@ import { IMegolmEncryptedContent, IncomingRoomKeyRequest, IEncryptedContent } fr
43
43
import { RoomKeyRequestState } from "../OutgoingRoomKeyRequestManager" ;
44
44
import { OlmGroupSessionExtraData } from "../../@types/crypto" ;
45
45
import { MatrixError } from "../../http-api" ;
46
+ import { immediate } from "../../utils" ;
46
47
47
48
// determine whether the key can be shared with invitees
48
49
export function isRoomSharedHistory ( room : Room ) : boolean {
@@ -73,7 +74,6 @@ export interface IOlmDevice<T = DeviceInfo> {
73
74
deviceInfo : T ;
74
75
}
75
76
76
- /* eslint-disable camelcase */
77
77
export interface IOutboundGroupSessionKey {
78
78
chain_index : number ;
79
79
key : string ;
@@ -106,7 +106,6 @@ interface IPayload extends Partial<IMessage> {
106
106
algorithm ?: string ;
107
107
sender_key ?: string ;
108
108
}
109
- /* eslint-enable camelcase */
110
109
111
110
interface SharedWithData {
112
111
// The identity key of the device we shared with
@@ -223,6 +222,7 @@ export class MegolmEncryption extends EncryptionAlgorithm {
223
222
private encryptionPreparation ?: {
224
223
promise : Promise < void > ;
225
224
startTime : number ;
225
+ cancel : ( ) => void ;
226
226
} ;
227
227
228
228
protected readonly roomId : string ;
@@ -974,30 +974,36 @@ export class MegolmEncryption extends EncryptionAlgorithm {
974
974
* send, in order to speed up sending of the message.
975
975
*
976
976
* @param room - the room the event is in
977
+ * @returns A function that, when called, will stop the preparation
977
978
*/
978
- public prepareToEncrypt ( room : Room ) : void {
979
+ public prepareToEncrypt ( room : Room ) : ( ) => void {
979
980
if ( room . roomId !== this . roomId ) {
980
981
throw new Error ( "MegolmEncryption.prepareToEncrypt called on unexpected room" ) ;
981
982
}
982
983
983
984
if ( this . encryptionPreparation != null ) {
984
985
// We're already preparing something, so don't do anything else.
985
- // FIXME: check if we need to restart
986
- // (https://github.com/matrix-org/matrix-js-sdk/issues/1255)
987
986
const elapsedTime = Date . now ( ) - this . encryptionPreparation . startTime ;
988
987
this . prefixedLogger . debug (
989
988
`Already started preparing to encrypt for this room ${ elapsedTime } ms ago, skipping` ,
990
989
) ;
991
- return ;
990
+ return this . encryptionPreparation . cancel ;
992
991
}
993
992
994
993
this . prefixedLogger . debug ( "Preparing to encrypt events" ) ;
995
994
995
+ let cancelled = false ;
996
+ const isCancelled = ( ) : boolean => cancelled ;
997
+
996
998
this . encryptionPreparation = {
997
999
startTime : Date . now ( ) ,
998
1000
promise : ( async ( ) : Promise < void > => {
999
1001
try {
1000
- const [ devicesInRoom , blocked ] = await this . getDevicesInRoom ( room ) ;
1002
+ // Attempt to enumerate the devices in room, and gracefully
1003
+ // handle cancellation if it occurs.
1004
+ const getDevicesResult = await this . getDevicesInRoom ( room , false , isCancelled ) ;
1005
+ if ( getDevicesResult === null ) return ;
1006
+ const [ devicesInRoom , blocked ] = getDevicesResult ;
1001
1007
1002
1008
if ( this . crypto . globalErrorOnUnknownDevices ) {
1003
1009
// Drop unknown devices for now. When the message gets sent, we'll
@@ -1016,7 +1022,16 @@ export class MegolmEncryption extends EncryptionAlgorithm {
1016
1022
delete this . encryptionPreparation ;
1017
1023
}
1018
1024
} ) ( ) ,
1025
+
1026
+ cancel : ( ) : void => {
1027
+ // The caller has indicated that the process should be cancelled,
1028
+ // so tell the promise that we'd like to halt, and reset the preparation state.
1029
+ cancelled = true ;
1030
+ delete this . encryptionPreparation ;
1031
+ } ,
1019
1032
} ;
1033
+
1034
+ return this . encryptionPreparation . cancel ;
1020
1035
}
1021
1036
1022
1037
/**
@@ -1165,17 +1180,32 @@ export class MegolmEncryption extends EncryptionAlgorithm {
1165
1180
*
1166
1181
* @param forceDistributeToUnverified - if set to true will include the unverified devices
1167
1182
* even if setting is set to block them (useful for verification)
1183
+ * @param isCancelled - will cause the procedure to abort early if and when it starts
1184
+ * returning `true`. If omitted, cancellation won't happen.
1168
1185
*
1169
- * @returns Promise which resolves to an array whose
1170
- * first element is a map from userId to deviceId to deviceInfo indicating
1186
+ * @returns Promise which resolves to `null`, or an array whose
1187
+ * first element is a { @link DeviceInfoMap} indicating
1171
1188
* the devices that messages should be encrypted to, and whose second
1172
1189
* element is a map from userId to deviceId to data indicating the devices
1173
- * that are in the room but that have been blocked
1190
+ * that are in the room but that have been blocked.
1191
+ * If `isCancelled` is provided and returns `true` while processing, `null`
1192
+ * will be returned.
1193
+ * If `isCancelled` is not provided, the Promise will never resolve to `null`.
1174
1194
*/
1195
+ private async getDevicesInRoom (
1196
+ room : Room ,
1197
+ forceDistributeToUnverified ?: boolean ,
1198
+ ) : Promise < [ DeviceInfoMap , IBlockedMap ] > ;
1199
+ private async getDevicesInRoom (
1200
+ room : Room ,
1201
+ forceDistributeToUnverified ?: boolean ,
1202
+ isCancelled ?: ( ) => boolean ,
1203
+ ) : Promise < null | [ DeviceInfoMap , IBlockedMap ] > ;
1175
1204
private async getDevicesInRoom (
1176
1205
room : Room ,
1177
1206
forceDistributeToUnverified = false ,
1178
- ) : Promise < [ DeviceInfoMap , IBlockedMap ] > {
1207
+ isCancelled ?: ( ) => boolean ,
1208
+ ) : Promise < null | [ DeviceInfoMap , IBlockedMap ] > {
1179
1209
const members = await room . getEncryptionTargetMembers ( ) ;
1180
1210
this . prefixedLogger . debug (
1181
1211
`Encrypting for users (shouldEncryptForInvitedMembers: ${ room . shouldEncryptForInvitedMembers ( ) } ):` ,
@@ -1201,6 +1231,11 @@ export class MegolmEncryption extends EncryptionAlgorithm {
1201
1231
// See https://github.com/vector-im/element-web/issues/2305 for details.
1202
1232
const devices = await this . crypto . downloadKeys ( roomMembers , false ) ;
1203
1233
const blocked : IBlockedMap = { } ;
1234
+
1235
+ if ( isCancelled ?.( ) === true ) {
1236
+ return null ;
1237
+ }
1238
+
1204
1239
// remove any blocked devices
1205
1240
for ( const userId in devices ) {
1206
1241
if ( ! devices . hasOwnProperty ( userId ) ) {
@@ -1213,6 +1248,11 @@ export class MegolmEncryption extends EncryptionAlgorithm {
1213
1248
continue ;
1214
1249
}
1215
1250
1251
+ // Yield prior to checking each device so that we don't block
1252
+ // updating/rendering for too long.
1253
+ // See https://github.com/vector-im/element-web/issues/21612
1254
+ if ( isCancelled !== undefined ) await immediate ( ) ;
1255
+ if ( isCancelled ?.( ) === true ) return null ;
1216
1256
const deviceTrust = this . crypto . checkDeviceTrust ( userId , deviceId ) ;
1217
1257
1218
1258
if (
0 commit comments