From 238700cbdb1d765922688b778c5d2d1546457d2e Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 22 Aug 2016 17:09:24 +0100 Subject: [PATCH 1/3] Send out megolm keys when we start a megolm session For now, pending the arrival of SPEC-138, this happens via inline messages in the room. --- lib/crypto-algorithms/base.js | 4 +- lib/crypto-algorithms/megolm.js | 86 +++++++++++++++++++++++++++++---- lib/crypto-algorithms/olm.js | 6 +-- lib/crypto.js | 5 ++ 4 files changed, 86 insertions(+), 15 deletions(-) diff --git a/lib/crypto-algorithms/base.js b/lib/crypto-algorithms/base.js index 888d901e9..14c6ba18c 100644 --- a/lib/crypto-algorithms/base.js +++ b/lib/crypto-algorithms/base.js @@ -50,12 +50,14 @@ module.exports.DECRYPTION_CLASSES = {}; * @param {string} params.deviceId The identifier for this device. * @param {module:crypto} params.crypto crypto core * @param {module:OlmDevice} params.olmDevice olm.js wrapper + * @param {module:base-apis~MatrixBaseApis} baseApis base matrix api interface * @param {string} params.roomId The ID of the room we will be sending to */ var EncryptionAlgorithm = function(params) { this._deviceId = params.deviceId; this._crypto = params.crypto; this._olmDevice = params.olmDevice; + this._baseApis = params.baseApis; this._roomId = params.roomId; }; /** */ @@ -81,7 +83,7 @@ EncryptionAlgorithm.prototype.initRoomEncryption = function(roomMembers) { * @method module:crypto-algorithms/base.EncryptionAlgorithm#encryptMessage * @abstract * - * @param {module:models/room?} room + * @param {module:models/room} room * @param {string} eventType * @param {object} plaintext event content * diff --git a/lib/crypto-algorithms/megolm.js b/lib/crypto-algorithms/megolm.js index e067c1288..9ffa194f9 100644 --- a/lib/crypto-algorithms/megolm.js +++ b/lib/crypto-algorithms/megolm.js @@ -46,10 +46,12 @@ utils.inherits(MegolmEncryption, base.EncryptionAlgorithm); /** * @private * + * @param {module:models/room} room + * * @return {module:client.Promise} Promise which resolves when setup is * complete. */ -MegolmEncryption.prototype._ensureOutboundSession = function() { +MegolmEncryption.prototype._ensureOutboundSession = function(room) { if (this._prepPromise) { // prep already in progress return this._prepPromise; @@ -61,8 +63,6 @@ MegolmEncryption.prototype._ensureOutboundSession = function() { } var session_id = this._olmDevice.createOutboundGroupSession(); - this._outboundSessionId = session_id; - var key = this._olmDevice.getOutboundGroupSessionKey(session_id); console.log( @@ -80,20 +80,88 @@ MegolmEncryption.prototype._ensureOutboundSession = function() { key.key, key.chain_index ); + // send the keys to each (unblocked) device in the room. + var payload = { + type: "m.key", + content: { + algorithm: olmlib.MEGOLM_ALGORITHM, + room_id: this._roomId, + session_id: session_id, + session_key: key.key, + chain_index: key.chain_index, + } + }; + + var roomMembers = utils.map(room.getJoinedMembers(), function(u) { + return u.userId; + }); + var self = this; - // TODO: initiate key-sharing - this._prepPromise = q.delay(3000).then(function() { - console.log("woop woop, we totally shared the keys"); + var txnBase = '' + (new Date().getTime()) + '.'; + var txnCtr = 0; + + // TODO: we need to give the user a chance to block any devices or users + // before we send them the keys; it's too late to download them here. + this._prepPromise = this._crypto.downloadKeys( + roomMembers, false + ).then(function(res) { + return self._crypto.ensureOlmSessionsForUsers(roomMembers); + }).then(function(devicemap) { + var promises = []; + for (var userId in devicemap) { + if (!devicemap.hasOwnProperty(userId)) { + continue; + } + + var devices = devicemap[userId]; + + for (var deviceId in devices) { + if (!devices.hasOwnProperty(deviceId)) { + continue; + } + + var deviceInfo = devices[deviceId].device; + var encryptedContent = olmlib.encryptMessageForDevices( + self._deviceId, + self._olmDevice, + [deviceInfo.getIdentityKey()], + payload + ); + + var txnId = txnBase + (txnCtr++); + + // TODO: send an OOB message. for now, send an in-band message. + var path = utils.encodeUri( + "/rooms/$roomId/send/m.room.encrypted/$txnId", { + $roomId: self._roomId, + $txnId: txnId, + } + ); + + // TODO: retries + var promise = self._baseApis._http.authedRequest( + undefined, "PUT", path, undefined, encryptedContent + ); + + promises.push(promise); + } + } + return q.all(promises); + }).then(function() { + // don't set this until the keys are sent successfully; if we get an + // error, the user can restart by resending the message. + self._outboundSessionId = session_id; + }).finally(function() { self._prepPromise = null; }); + return this._prepPromise; }; - /** * @inheritdoc * - * @param {module:models/room?} room + * @param {module:models/room} room * @param {string} eventType * @param {object} plaintext event content * @@ -101,7 +169,7 @@ MegolmEncryption.prototype._ensureOutboundSession = function() { */ MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) { var self = this; - return this._ensureOutboundSession().then(function() { + return this._ensureOutboundSession(room).then(function() { var payloadJson = { room_id: self._roomId, type: eventType, diff --git a/lib/crypto-algorithms/olm.js b/lib/crypto-algorithms/olm.js index 0030476fb..c6fb1f88a 100644 --- a/lib/crypto-algorithms/olm.js +++ b/lib/crypto-algorithms/olm.js @@ -56,17 +56,13 @@ OlmEncryption.prototype.initRoomEncryption = function(roomMembers) { /** * @inheritdoc * - * @param {module:models/room?} room + * @param {module:models/room} room * @param {string} eventType * @param {object} plaintext event content * * @return {module:client.Promise} Promise which resolves to the new event body */ OlmEncryption.prototype.encryptMessage = function(room, eventType, content) { - if (!room) { - throw new Error("Cannot send encrypted messages in unknown rooms"); - } - // pick the list of recipients based on the membership list. // // TODO: there is a race condition here! What if a new user turns up diff --git a/lib/crypto.js b/lib/crypto.js index 34e532ce0..f43d9c0b4 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -540,6 +540,7 @@ Crypto.prototype.setRoomEncryption = function(roomId, config, roomMembers) { deviceId: this._deviceId, crypto: this, olmDevice: this._olmDevice, + baseApis: this._baseApis, roomId: roomId, }); this._roomAlgorithms[roomId] = alg; @@ -655,6 +656,10 @@ Crypto.prototype.encryptEventIfNeeded = function(event, room) { return null; } + if (!room) { + throw new Error("Cannot send encrypted messages in unknown rooms"); + } + var roomId = event.getRoomId(); var alg = this._roomAlgorithms[roomId]; From 9f180179d50e795e2a148faf3c1e5387c27f2072 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Mon, 22 Aug 2016 18:13:11 +0100 Subject: [PATCH 2/3] rename m.key event to m.room_key ... because m.key is scary, or something --- lib/crypto-algorithms/megolm.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/crypto-algorithms/megolm.js b/lib/crypto-algorithms/megolm.js index 9ffa194f9..ef7ab511f 100644 --- a/lib/crypto-algorithms/megolm.js +++ b/lib/crypto-algorithms/megolm.js @@ -82,7 +82,7 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) { // send the keys to each (unblocked) device in the room. var payload = { - type: "m.key", + type: "m.room_key", content: { algorithm: olmlib.MEGOLM_ALGORITHM, room_id: this._roomId, From e708e59b1535b10baefb648047d0335d775ca719 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 23 Aug 2016 17:27:47 +0100 Subject: [PATCH 3/3] Add a TODO about batching events --- lib/crypto-algorithms/megolm.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/crypto-algorithms/megolm.js b/lib/crypto-algorithms/megolm.js index ef7ab511f..908b0e623 100644 --- a/lib/crypto-algorithms/megolm.js +++ b/lib/crypto-algorithms/megolm.js @@ -131,6 +131,12 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) { var txnId = txnBase + (txnCtr++); // TODO: send an OOB message. for now, send an in-band message. + + // TODO: aggregate the messages into batches. If we make a + // separate request for each message, we will get rate-limited. + // On the other hand, we can't just send them in one big batch, + // because we'll hit the event size limit. + var path = utils.encodeUri( "/rooms/$roomId/send/m.room.encrypted/$txnId", { $roomId: self._roomId,