1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-28 05:03:59 +03:00

Make Crypto._processReceivedRoomKeyRequests async

This is slightly complicated by the fact that it's initiated from a synchronous
process which we don't want to make async (processing the /sync response) and
we want to avoid racing two copies of the processor.
This commit is contained in:
Richard van der Hoff
2017-07-19 16:52:55 +01:00
parent 14ad32bcd2
commit 25ccd6bc6d

View File

@@ -104,6 +104,8 @@ function Crypto(baseApis, sessionStore, userId, deviceId,
// we received in the current sync. // we received in the current sync.
this._receivedRoomKeyRequests = []; this._receivedRoomKeyRequests = [];
this._receivedRoomKeyRequestCancellations = []; this._receivedRoomKeyRequestCancellations = [];
// true if we are currently processing received room key requests
this._processingRoomKeyRequests = false;
let myDevices = this._sessionStore.getEndToEndDevicesForUser( let myDevices = this._sessionStore.getEndToEndDevicesForUser(
this._userId, this._userId,
@@ -1059,79 +1061,124 @@ Crypto.prototype._onRoomKeyRequestEvent = function(event) {
* *
* @private * @private
*/ */
Crypto.prototype._processReceivedRoomKeyRequests = function() { Crypto.prototype._processReceivedRoomKeyRequests = async function() {
const requests = this._receivedRoomKeyRequests; if (this._processingRoomKeyRequests) {
this._receivedRoomKeyRequests = []; // we're still processing last time's requests; keep queuing new ones
for (const req of requests) { // up for now.
const userId = req.userId; return;
const deviceId = req.deviceId; }
this._processingRoomKeyRequests = true;
const body = req.requestBody; try {
const roomId = body.room_id; // we need to grab and clear the queues in the synchronous bit of this method,
const alg = body.algorithm; // so that we don't end up racing with the next /sync.
const requests = this._receivedRoomKeyRequests;
this._receivedRoomKeyRequests = [];
const cancellations = this._receivedRoomKeyRequestCancellations;
this._receivedRoomKeyRequestCancellations = [];
console.log(`m.room_key_request from ${userId}:${deviceId}` + // Process all of the requests, *then* all of the cancellations.
//
// This makes sure that if we get a request and its cancellation in the
// same /sync result, then we process the request before the
// cancellation (and end up with a cancelled request), rather than the
// cancellation before the request (and end up with an outstanding
// request which should have been cancelled.)
await Promise.map(
requests, (req) =>
this._processReceivedRoomKeyRequest(req),
);
await Promise.map(
cancellations, (cancellation) =>
this._processReceivedRoomKeyRequestCancellation(cancellation),
);
} catch (e) {
console.error(`Error processing room key requsts: ${e}`);
} finally {
this._processingRoomKeyRequests = false;
}
};
/**
* Helper for processReceivedRoomKeyRequests
*
* @param {IncomingRoomKeyRequest} req
*/
Crypto.prototype._processReceivedRoomKeyRequest = async function(req) {
const userId = req.userId;
const deviceId = req.deviceId;
const body = req.requestBody;
const roomId = body.room_id;
const alg = body.algorithm;
console.log(`m.room_key_request from ${userId}:${deviceId}` +
` for ${roomId} / ${body.session_id} (id ${req.requestId})`); ` for ${roomId} / ${body.session_id} (id ${req.requestId})`);
if (userId !== this._userId) { if (userId !== this._userId) {
// TODO: determine if we sent this device the keys already: in // TODO: determine if we sent this device the keys already: in
// which case we can do so again. // which case we can do so again.
console.log("Ignoring room key request from other user for now"); console.log("Ignoring room key request from other user for now");
return; return;
}
// todo: should we queue up requests we don't yet have keys for,
// in case they turn up later?
// if we don't have a decryptor for this room/alg, we don't have
// the keys for the requested events, and can drop the requests.
if (!this._roomDecryptors[roomId]) {
console.log(`room key request for unencrypted room ${roomId}`);
continue;
}
const decryptor = this._roomDecryptors[roomId][alg];
if (!decryptor) {
console.log(`room key request for unknown alg ${alg} in room ${roomId}`);
continue;
}
if (!decryptor.hasKeysForKeyRequest(req)) {
console.log(
`room key request for unknown session ${roomId} / ` +
body.session_id,
);
continue;
}
req.share = () => {
decryptor.shareKeysWithDevice(req);
};
// if the device is is verified already, share the keys
const device = this._deviceList.getStoredDevice(userId, deviceId);
if (device && device.isVerified()) {
console.log('device is already verified: sharing keys');
req.share();
return;
}
this.emit("crypto.roomKeyRequest", req);
} }
const cancellations = this._receivedRoomKeyRequestCancellations; // todo: should we queue up requests we don't yet have keys for,
this._receivedRoomKeyRequestCancellations = []; // in case they turn up later?
for (const cancellation of cancellations) {
// if we don't have a decryptor for this room/alg, we don't have
// the keys for the requested events, and can drop the requests.
if (!this._roomDecryptors[roomId]) {
console.log(`room key request for unencrypted room ${roomId}`);
return;
}
const decryptor = this._roomDecryptors[roomId][alg];
if (!decryptor) {
console.log(`room key request for unknown alg ${alg} in room ${roomId}`);
return;
}
if (!decryptor.hasKeysForKeyRequest(req)) {
console.log( console.log(
`m.room_key_request cancellation for ${cancellation.userId}:` + `room key request for unknown session ${roomId} / ` +
`${cancellation.deviceId} (id ${cancellation.requestId})`, body.session_id,
); );
return;
// we should probably only notify the app of cancellations we told it
// about, but we don't currently have a record of that, so we just pass
// everything through.
this.emit("crypto.roomKeyRequestCancellation", cancellation);
} }
req.share = () => {
decryptor.shareKeysWithDevice(req);
};
// if the device is is verified already, share the keys
const device = this._deviceList.getStoredDevice(userId, deviceId);
if (device && device.isVerified()) {
console.log('device is already verified: sharing keys');
req.share();
return;
}
this.emit("crypto.roomKeyRequest", req);
};
/**
* Helper for processReceivedRoomKeyRequests
*
* @param {IncomingRoomKeyRequestCancellation} cancellation
*/
Crypto.prototype._processReceivedRoomKeyRequestCancellation = async function(
cancellation,
) {
console.log(
`m.room_key_request cancellation for ${cancellation.userId}:` +
`${cancellation.deviceId} (id ${cancellation.requestId})`,
);
// we should probably only notify the app of cancellations we told it
// about, but we don't currently have a record of that, so we just pass
// everything through.
this.emit("crypto.roomKeyRequestCancellation", cancellation);
}; };
/** /**