You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
Make encryption asynchronous
We're going to need to send out a load of messages to distribute the megolm keys; as a first step, deal with the asynchronicity this will require.
This commit is contained in:
@@ -822,10 +822,18 @@ function _sendEvent(client, room, event, callback) {
|
||||
// so that we can handle synchronous and asynchronous exceptions with the
|
||||
// same code path.
|
||||
return q().then(function() {
|
||||
var encryptionPromise = null;
|
||||
if (client._crypto) {
|
||||
client._crypto.encryptEventIfNeeded(event, room);
|
||||
encryptionPromise = client._crypto.encryptEventIfNeeded(event, room);
|
||||
}
|
||||
|
||||
if (encryptionPromise) {
|
||||
_updatePendingEventStatus(room, event, EventStatus.ENCRYPTING);
|
||||
encryptionPromise = encryptionPromise.then(function() {
|
||||
_updatePendingEventStatus(room, event, EventStatus.SENDING);
|
||||
});
|
||||
}
|
||||
return encryptionPromise;
|
||||
}).then(function() {
|
||||
var promise;
|
||||
// this event may be queued
|
||||
if (client.scheduler) {
|
||||
@@ -857,10 +865,14 @@ function _sendEvent(client, room, event, callback) {
|
||||
// the request failed to send.
|
||||
console.error("Error sending event", err.stack || err);
|
||||
|
||||
_updatePendingEventStatus(room, event, EventStatus.NOT_SENT);
|
||||
try {
|
||||
_updatePendingEventStatus(room, event, EventStatus.NOT_SENT);
|
||||
|
||||
if (callback) {
|
||||
callback(err);
|
||||
if (callback) {
|
||||
callback(err);
|
||||
}
|
||||
} catch (err2) {
|
||||
console.error("Exception in error handler!", err2.stack || err);
|
||||
}
|
||||
throw err;
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ limitations under the License.
|
||||
*
|
||||
* @module crypto-algorithms/base
|
||||
*/
|
||||
var q = require("q");
|
||||
|
||||
var utils = require("../utils");
|
||||
|
||||
@@ -43,6 +44,7 @@ module.exports.DECRYPTION_CLASSES = {};
|
||||
* base type for encryption implementations
|
||||
*
|
||||
* @constructor
|
||||
* @alias module:crypto-algorithms/base.EncryptionAlgorithm
|
||||
*
|
||||
* @param {object} params parameters
|
||||
* @param {string} params.deviceId The identifier for this device.
|
||||
@@ -50,22 +52,28 @@ module.exports.DECRYPTION_CLASSES = {};
|
||||
* @param {module:OlmDevice} params.olmDevice olm.js wrapper
|
||||
* @param {string} params.roomId The ID of the room we will be sending to
|
||||
*/
|
||||
module.exports.EncryptionAlgorithm = function(params) {
|
||||
var EncryptionAlgorithm = function(params) {
|
||||
this._deviceId = params.deviceId;
|
||||
this._crypto = params.crypto;
|
||||
this._olmDevice = params.olmDevice;
|
||||
this._roomId = params.roomId;
|
||||
};
|
||||
/** */
|
||||
module.exports.EncryptionAlgorithm = EncryptionAlgorithm;
|
||||
|
||||
/**
|
||||
* Initialise this EncryptionAlgorithm instance for a particular room
|
||||
* Initialise this EncryptionAlgorithm instance for a particular room.
|
||||
*
|
||||
* @method module:crypto-algorithms/base.EncryptionAlgorithm#initRoomEncryption
|
||||
* @abstract
|
||||
* <p>This will be called once per EncryptionAlgorithm, just after the
|
||||
* constructor is called.
|
||||
*
|
||||
* @param {string[]} roomMembers list of currently-joined users in the room
|
||||
* @return {module:client.Promise} Promise which resolves when setup is complete
|
||||
*/
|
||||
EncryptionAlgorithm.prototype.initRoomEncryption = function(roomMembers) {
|
||||
return q();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt a message event
|
||||
@@ -77,7 +85,7 @@ module.exports.EncryptionAlgorithm = function(params) {
|
||||
* @param {string} eventType
|
||||
* @param {object} plaintext event content
|
||||
*
|
||||
* @return {object} new event body
|
||||
* @return {module:client.Promise} Promise which resolves to the new event body
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@@ -39,35 +39,33 @@ var MEGOLM_ALGORITHM = "m.megolm.v1.aes-sha2";
|
||||
*/
|
||||
function MegolmEncryption(params) {
|
||||
base.EncryptionAlgorithm.call(this, params);
|
||||
this._prepPromise = null;
|
||||
this._outboundSessionId = null;
|
||||
}
|
||||
utils.inherits(MegolmEncryption, base.EncryptionAlgorithm);
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
* @param {string[]} roomMembers list of currently-joined users in the room
|
||||
* @return {module:client.Promise} Promise which resolves when setup is complete
|
||||
*/
|
||||
MegolmEncryption.prototype.initRoomEncryption = function(roomMembers) {
|
||||
// nothing to do here.
|
||||
return q();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @private
|
||||
*
|
||||
* @return {module:client.Promise} Promise which resolves when setup is
|
||||
* complete.
|
||||
*/
|
||||
MegolmEncryption.prototype._ensureOutboundSession = function() {
|
||||
if (this._outboundSessionId) {
|
||||
return;
|
||||
if (this._prepPromise) {
|
||||
// prep already in progress
|
||||
return this._prepPromise;
|
||||
}
|
||||
|
||||
if (this._outboundSessionId) {
|
||||
// prep already done
|
||||
return q();
|
||||
}
|
||||
|
||||
var session_id = this._olmDevice.createOutboundGroupSession();
|
||||
this._outboundSessionId = session_id;
|
||||
|
||||
var key = this._olmDevice.getOutboundGroupSessionKey(session_id);
|
||||
|
||||
// TODO: initiate key-sharing
|
||||
|
||||
console.log(
|
||||
'Created outbound session. Add with window.mxMatrixClientPeg.' +
|
||||
'matrixClient._crypto._olmDevice.addInboundGroupSession("' +
|
||||
@@ -82,8 +80,17 @@ MegolmEncryption.prototype._ensureOutboundSession = function() {
|
||||
this._roomId, this._olmDevice.deviceCurve25519Key, session_id,
|
||||
key.key, key.chain_index
|
||||
);
|
||||
|
||||
var self = this;
|
||||
// TODO: initiate key-sharing
|
||||
this._prepPromise = q.delay(3000).then(function() {
|
||||
console.log("woop woop, we totally shared the keys");
|
||||
self._prepPromise = null;
|
||||
});
|
||||
return this._prepPromise;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* @inheritdoc
|
||||
*
|
||||
@@ -91,30 +98,31 @@ MegolmEncryption.prototype._ensureOutboundSession = function() {
|
||||
* @param {string} eventType
|
||||
* @param {object} plaintext event content
|
||||
*
|
||||
* @return {object} new event body
|
||||
* @return {module:client.Promise} Promise which resolves to the new event body
|
||||
*/
|
||||
MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||
this._ensureOutboundSession();
|
||||
var self = this;
|
||||
return this._ensureOutboundSession().then(function() {
|
||||
var payloadJson = {
|
||||
room_id: self._roomId,
|
||||
type: eventType,
|
||||
content: content
|
||||
};
|
||||
|
||||
var payloadJson = {
|
||||
room_id: this._roomId,
|
||||
type: eventType,
|
||||
content: content
|
||||
};
|
||||
var ciphertext = self._olmDevice.encryptGroupMessage(
|
||||
self._outboundSessionId, JSON.stringify(payloadJson)
|
||||
);
|
||||
|
||||
var ciphertext = this._olmDevice.encryptGroupMessage(
|
||||
this._outboundSessionId, JSON.stringify(payloadJson)
|
||||
);
|
||||
var encryptedContent = {
|
||||
algorithm: MEGOLM_ALGORITHM,
|
||||
sender_key: self._olmDevice.deviceCurve25519Key,
|
||||
body: ciphertext,
|
||||
session_id: self._outboundSessionId,
|
||||
signature: "FIXME",
|
||||
};
|
||||
|
||||
var encryptedContent = {
|
||||
algorithm: MEGOLM_ALGORITHM,
|
||||
sender_key: this._olmDevice.deviceCurve25519Key,
|
||||
body: ciphertext,
|
||||
session_id: this._outboundSessionId,
|
||||
signature: "FIXME",
|
||||
};
|
||||
|
||||
return encryptedContent;
|
||||
return encryptedContent;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,6 +20,7 @@ limitations under the License.
|
||||
*
|
||||
* @module crypto-algorithms/olm
|
||||
*/
|
||||
var q = require('q');
|
||||
|
||||
var utils = require("../utils");
|
||||
|
||||
@@ -60,7 +61,7 @@ OlmEncryption.prototype.initRoomEncryption = function(roomMembers) {
|
||||
* @param {string} eventType
|
||||
* @param {object} plaintext event content
|
||||
*
|
||||
* @return {object} new event body
|
||||
* @return {module:client.Promise} Promise which resolves to the new event body
|
||||
*/
|
||||
OlmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||
if (!room) {
|
||||
@@ -128,7 +129,7 @@ OlmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||
sender_key: this._olmDevice.deviceCurve25519Key,
|
||||
ciphertext: ciphertext
|
||||
};
|
||||
return encryptedContent;
|
||||
return q(encryptedContent);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -600,7 +600,7 @@ Crypto.prototype.isSenderKeyVerified = function(userId, algorithm, sender_key) {
|
||||
* @param {object} config The encryption config for the room.
|
||||
* @param {string[]} roomMembers userIds of room members to start sessions with
|
||||
*
|
||||
* @return {Object} A promise that will resolve when encryption is setup.
|
||||
* @return {module:client.Promise} A promise that will resolve when encryption is setup.
|
||||
*/
|
||||
Crypto.prototype.setRoomEncryption = function(roomId, config, roomMembers) {
|
||||
// if we already have encryption in this room, we should ignore this event
|
||||
@@ -727,18 +727,21 @@ Crypto.prototype.isRoomEncrypted = function(roomId) {
|
||||
*
|
||||
* @param {module:models/room?} room destination room. Null if the destination
|
||||
* is not a room we have seen over the sync pipe.
|
||||
*
|
||||
* @return {module:client.Promise?} Promise which resolves when the event has been
|
||||
* encrypted, or null if nothing was needed
|
||||
*/
|
||||
Crypto.prototype.encryptEventIfNeeded = function(event, room) {
|
||||
if (event.isEncrypted()) {
|
||||
// this event has already been encrypted; this happens if the
|
||||
// encryption step succeeded, but the send step failed on the first
|
||||
// attempt.
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (event.getType() !== "m.room.message") {
|
||||
// we only encrypt m.room.message
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
var roomId = event.getRoomId();
|
||||
@@ -755,13 +758,14 @@ Crypto.prototype.encryptEventIfNeeded = function(event, room) {
|
||||
"configuration event."
|
||||
);
|
||||
}
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
var encryptedContent = alg.encryptMessage(
|
||||
return alg.encryptMessage(
|
||||
room, event.getType(), event.getContent()
|
||||
);
|
||||
event.makeEncrypted("m.room.encrypted", encryptedContent);
|
||||
).then(function(encryptedContent) {
|
||||
event.makeEncrypted("m.room.encrypted", encryptedContent);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -29,6 +29,10 @@ limitations under the License.
|
||||
module.exports.EventStatus = {
|
||||
/** The event was not sent and will no longer be retried. */
|
||||
NOT_SENT: "not_sent",
|
||||
|
||||
/** The message is being encrypted */
|
||||
ENCRYPTING: "encrypting",
|
||||
|
||||
/** The event is in the process of being sent. */
|
||||
SENDING: "sending",
|
||||
/** The event is in a queue waiting to be sent. */
|
||||
|
||||
@@ -845,8 +845,17 @@ Room.prototype._handleRemoteEcho = function(remoteEvent, localEvent) {
|
||||
*/
|
||||
var ALLOWED_TRANSITIONS = {};
|
||||
|
||||
ALLOWED_TRANSITIONS[EventStatus.SENDING] =
|
||||
[EventStatus.QUEUED, EventStatus.NOT_SENT, EventStatus.SENT];
|
||||
ALLOWED_TRANSITIONS[EventStatus.ENCRYPTING] = [
|
||||
EventStatus.SENDING,
|
||||
EventStatus.NOT_SENT,
|
||||
];
|
||||
|
||||
ALLOWED_TRANSITIONS[EventStatus.SENDING] = [
|
||||
EventStatus.ENCRYPTING,
|
||||
EventStatus.QUEUED,
|
||||
EventStatus.NOT_SENT,
|
||||
EventStatus.SENT,
|
||||
];
|
||||
|
||||
ALLOWED_TRANSITIONS[EventStatus.QUEUED] =
|
||||
[EventStatus.SENDING, EventStatus.CANCELLED];
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
"use strict";
|
||||
var q = require("q");
|
||||
var sdk = require("../..");
|
||||
var HttpBackend = require("../mock-request");
|
||||
var utils = require("../test-utils");
|
||||
@@ -69,8 +68,8 @@ describe("MatrixClient retrying", function() {
|
||||
expect(ev1.status).toEqual(EventStatus.SENDING);
|
||||
expect(ev2.status).toEqual(EventStatus.SENDING);
|
||||
|
||||
// give the reactor a chance to run, so that ev2 gets queued
|
||||
q().then(function() {
|
||||
// the first message should get sent, and the second should get queued
|
||||
httpBackend.when("PUT", "/send/m.room.message/").check(function(rq) {
|
||||
// ev2 should now have been queued
|
||||
expect(ev2.status).toEqual(EventStatus.QUEUED);
|
||||
|
||||
@@ -82,12 +81,9 @@ describe("MatrixClient retrying", function() {
|
||||
// shouldn't be able to cancel the first message yet
|
||||
expect(function() { client.cancelPendingEvent(ev1); })
|
||||
.toThrow();
|
||||
}).respond(400); // fail the first message
|
||||
|
||||
// fail the first send
|
||||
httpBackend.when("PUT", "/send/m.room.message/")
|
||||
.respond(400);
|
||||
return httpBackend.flush();
|
||||
}).then(function() {
|
||||
httpBackend.flush().then(function() {
|
||||
expect(ev1.status).toEqual(EventStatus.NOT_SENT);
|
||||
expect(tl.length).toEqual(1);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user