diff --git a/lib/crypto/OlmDevice.js b/lib/crypto/OlmDevice.js index ca9565fe3..5d2fe6620 100644 --- a/lib/crypto/OlmDevice.js +++ b/lib/crypto/OlmDevice.js @@ -539,7 +539,9 @@ OlmDevice.prototype._saveInboundGroupSession = function( * @param {string} senderKey * @param {string} sessionId * @param {function} func - * @return {object} result of func + * @return {object} Object with two keys "result": result of func, "exists" + * whether the session exists. if the session doesn't exist then the function + * isn't called and the "result" is undefined. * @private */ OlmDevice.prototype._getInboundGroupSession = function( @@ -550,7 +552,7 @@ OlmDevice.prototype._getInboundGroupSession = function( ); if (r === null) { - throw new Error("Unknown inbound group session id"); + return {sessionExists: false} } r = JSON.parse(r); @@ -567,7 +569,7 @@ OlmDevice.prototype._getInboundGroupSession = function( var session = new Olm.InboundGroupSession(); try { session.unpickle(this._pickleKey, r.session); - return func(session); + return {sessionExists: true, result: func(session)}; } finally { session.free(); } @@ -603,7 +605,7 @@ OlmDevice.prototype.addInboundGroupSession = function( * @param {string} sessionId session identifier * @param {string} body base64-encoded body of the encrypted message * - * @return {string} plaintext + * @return {object} {result: "plaintext"|undefined, sessionExists: Boolean} */ OlmDevice.prototype.decryptGroupMessage = function( roomId, senderKey, sessionId, body diff --git a/lib/crypto/algorithms/megolm.js b/lib/crypto/algorithms/megolm.js index b10794524..56d7bf226 100644 --- a/lib/crypto/algorithms/megolm.js +++ b/lib/crypto/algorithms/megolm.js @@ -356,7 +356,8 @@ utils.inherits(MegolmDecryption, base.DecryptionAlgorithm); * * @param {object} event raw event * - * @return {object} decrypted payload (with properties 'type', 'content') + * @return {object} object with 'result' key with decrypted payload (with + * properties 'type', 'content') and a 'sessionKey' key. * * @throws {module:crypto/algorithms/base.DecryptionError} if there is a * problem decrypting the event @@ -377,7 +378,11 @@ MegolmDecryption.prototype.decryptEvent = function(event) { var res = this._olmDevice.decryptGroupMessage( event.room_id, content.sender_key, content.session_id, content.ciphertext ); - return JSON.parse(res); + if (res.sessionExists) { + return {result: JSON.parse(res.result), sessionExists: true}; + } else { + return {sessionExists: false}; + } } catch (e) { throw new base.DecryptionError(e); } diff --git a/lib/crypto/algorithms/olm.js b/lib/crypto/algorithms/olm.js index d5fd4fd4e..e75877cdf 100644 --- a/lib/crypto/algorithms/olm.js +++ b/lib/crypto/algorithms/olm.js @@ -142,7 +142,9 @@ utils.inherits(OlmDecryption, base.DecryptionAlgorithm); * * @param {object} event raw event * - * @return {object} decrypted payload (with properties 'type', 'content') + * @return {object} result object with result property with the decrypted + * payload (with properties 'type', 'content'), and a "sessionExists" key + * always set to true. * * @throws {module:crypto/algorithms/base.DecryptionError} if there is a * problem decrypting the event @@ -198,7 +200,7 @@ OlmDecryption.prototype.decryptEvent = function(event) { // TODO: Check the sender user id matches the sender key. // TODO: check the room_id and fingerprint if (payloadString !== null) { - return JSON.parse(payloadString); + return {result: JSON.parse(payloadString), sessionExists: true}; } else { throw new base.DecryptionError("Bad Encrypted Message"); } diff --git a/lib/crypto/index.js b/lib/crypto/index.js index f5184c40b..00284064d 100644 --- a/lib/crypto/index.js +++ b/lib/crypto/index.js @@ -820,7 +820,41 @@ Crypto.prototype.decryptEvent = function(event) { var alg = new AlgClass({ olmDevice: this._olmDevice, }); - return alg.decryptEvent(event); + var r = alg.decryptEvent(event); + if (r.sessionExists) { + return r.result; + } else { + // We've got a message for a session we don't have. + // Maybe the sender forgot to tell us about the session. + // Remind the sender that we exists so that they might + // tell us about the sender. + if (event.getRoomId !== undefined && event.getSender !== undefined) { + var senderUserId = event.getSender(); + var roomId = event.getRoomId(); + var content = {}; + var senderDeviceId = event.content.device_id; + if (senderDeviceId !== undefined) { + content[senderUserId][senderDeviceId] = { + device_id: this._deviceId, + rooms: [roomId], + }; + } else { + content[senderUserId]["*"] = { + device_id: this._deviceId, + rooms: [roomId], + }; + } + // TODO: Ratelimit the "m.new_device" messages to make sure we don't + // flood the target device with messages if we get lots of encrypted + // messages from them at once. + this._baseApis.sendToDevice( + "m.new_device", // OH HAI! + content + ).done(function() {}); + } + + throw new algorithms.DecryptionError("Unknown inbound session id"); + } }; /**