From 2c9f8ba5985f6a25c7f938b92880cb60daa27a83 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Tue, 16 Aug 2016 14:29:15 +0100 Subject: [PATCH] Factor Olm encryption/decryption out to new classes - to make way for alternative encryption algorithms. We now store an encryption object for each room, rather than referring to sessionstore on each event. Also a little light tidying to the jsdocs. --- lib/OlmDevice.js | 8 ++ lib/client.js | 2 +- lib/crypto-algorithms/base.js | 137 ++++++++++++++++++++ lib/crypto-algorithms/index.js | 39 ++++++ lib/crypto-algorithms/olm.js | 218 ++++++++++++++++++++++++++++++++ lib/crypto.js | 223 +++++++-------------------------- lib/matrix.js | 2 +- lib/models/room.js | 3 +- lib/timeline-window.js | 2 +- 9 files changed, 455 insertions(+), 179 deletions(-) create mode 100644 lib/crypto-algorithms/base.js create mode 100644 lib/crypto-algorithms/index.js create mode 100644 lib/crypto-algorithms/olm.js diff --git a/lib/OlmDevice.js b/lib/OlmDevice.js index a040ca522..f0df58523 100644 --- a/lib/OlmDevice.js +++ b/lib/OlmDevice.js @@ -15,6 +15,12 @@ limitations under the License. */ "use strict"; +/** + * olm.js wrapper + * + * @module OlmDevice + */ + var Olm = require("olm"); var utils = require("./utils"); @@ -25,6 +31,8 @@ var utils = require("./utils"); * Accounts and sessions are kept pickled in a sessionStore. * * @constructor + * @alias module:OlmDevice + * * @param {Object} sessionStore A store to be used for data in end-to-end * crypto * diff --git a/lib/client.js b/lib/client.js index 3a77c1d9a..31e4de826 100644 --- a/lib/client.js +++ b/lib/client.js @@ -2315,7 +2315,7 @@ MatrixClient.prototype.getTurnServers = function() { * appear in a room's timeline. If "chronological", messages will appear * in the timeline when the call to sendEvent was made. If * "detached", pending messages will appear in a separate list, - * accessbile via {@link module:models/room~Room#getPendingEvents}. Default: + * accessbile via {@link module:models/room#getPendingEvents}. Default: * "chronological". * * @param {Number=} opts.pollTimeout The number of milliseconds to wait on /events. diff --git a/lib/crypto-algorithms/base.js b/lib/crypto-algorithms/base.js new file mode 100644 index 000000000..95b335ff5 --- /dev/null +++ b/lib/crypto-algorithms/base.js @@ -0,0 +1,137 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +"use strict"; + +/** + * Internal module. Defines the base classes of the encryption implementations + * + * @module crypto-algorithms/base + */ + +var utils = require("../utils"); + +/** + * map of registered encryption algorithm classes. A map from string to {@link + * module:crypto-algorithms/base.EncryptionAlgorithm|EncryptionAlgorithm} class + * + * @type {Object.} + */ +module.exports.ENCRYPTION_CLASSES = {}; + +/** + * map of registered encryption algorithm classes. Map from string to {@link + * module:crypto-algorithms/base.DecryptionAlgorithm|DecryptionAlgorithm} class + * + * @type {Object.} + */ +module.exports.DECRYPTION_CLASSES = {}; + +/** + * base type for encryption implementations + * + * @constructor + * + * @param {string} deviceId The identifier for this device. + * @param {module:crypto} crypto crypto core + * @param {module:OlmDevice} olmDevice olm.js wrapper + */ +module.exports.EncryptionAlgorithm = function(deviceId, crypto, olmDevice) { + this._deviceId = deviceId; + this._crypto = crypto; + this._olmDevice = olmDevice; +}; + +/** + * Initialise this EncryptionAlgorithm instance for a particular room + * + * @method module:crypto-algorithms/base.EncryptionAlgorithm#initRoomEncryption + * @abstract + * + * @param {string[]} roomMembers list of currently-joined users in the room + * @return {module:client.Promise} Promise which resolves when setup is complete + */ + +/** + * Encrypt a message event + * + * @method module:crypto-algorithms/base.EncryptionAlgorithm#encryptMessage + * @abstract + * + * @param {module:models/room} room + * @param {string} eventType + * @param {object} plaintext event content + * + * @return {object} new event body + */ + + +/** + * base type for decryption implementations + * + * @constructor + * @param {string} deviceId The identifier for this device. + * @param {module:crypto} crypto crypto core + * @param {module:OlmDevice} olmDevice olm.js wrapper + */ +module.exports.DecryptionAlgorithm = function(deviceId, crypto, olmDevice) { + this._deviceId = deviceId; + this._crypto = crypto; + this._olmDevice = olmDevice; +}; + +/** + * Decrypt an event + * + * @method module:crypto-algorithms/base.DecryptionAlgorithm#decryptEvent + * @abstract + * + * @param {object} event raw event + * + * @return {object} decrypted payload (with properties 'type', 'content') + * + * @throws {module:crypto-algorithms/base.DecryptionError} if there is a + * problem decrypting the event + */ + +/** + * Exception thrown when decryption fails + * + * @constructor + * @param {string} msg message describing the problem + * @extends Error + */ +module.exports.DecryptionError = function(msg) { + this.message = msg; +}; +utils.inherits(module.exports.DecryptionError, Error); + +/** + * Registers an encryption/decryption class for a particular algorithm + * + * @param {string} algorithm algorithm tag to register for + * + * @param {class} encryptor {@link + * module:crypto-algorithms/base.EncryptionAlgorithm|EncryptionAlgorithm} + * implementation + * + * @param {class} decryptor {@link + * module:crypto-algorithms/base.DecryptionAlgorithm|DecryptionAlgorithm} + * implementation + */ +module.exports.registerAlgorithm = function(algorithm, encryptor, decryptor) { + module.exports.ENCRYPTION_CLASSES[algorithm] = encryptor; + module.exports.DECRYPTION_CLASSES[algorithm] = decryptor; +}; diff --git a/lib/crypto-algorithms/index.js b/lib/crypto-algorithms/index.js new file mode 100644 index 000000000..6a35c366f --- /dev/null +++ b/lib/crypto-algorithms/index.js @@ -0,0 +1,39 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +"use strict"; + +/** + * @module crypto-algorithms + */ + +var base = require("./base"); + +require("./olm"); + +/** + * @see module:crypto-algorithms/base.ENCRYPTION_CLASSES + */ +module.exports.ENCRYPTION_CLASSES = base.ENCRYPTION_CLASSES; + +/** + * @see module:crypto-algorithms/base.DECRYPTION_CLASSES + */ +module.exports.DECRYPTION_CLASSES = base.DECRYPTION_CLASSES; + +/** + * @see module:crypto-algorithms/base.DecryptionError + */ +module.exports.DecryptionError = base.DecryptionError; diff --git a/lib/crypto-algorithms/olm.js b/lib/crypto-algorithms/olm.js new file mode 100644 index 000000000..b8229c9b3 --- /dev/null +++ b/lib/crypto-algorithms/olm.js @@ -0,0 +1,218 @@ +/* +Copyright 2016 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +"use strict"; + +/** + * Defines m.olm encryption/decryption + * + * @module crypto-algorithms/olm + */ + +var utils = require("../utils"); + +var base = require("./base"); + +var OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2"; + +/** + * Olm encryption implementation + * + * @constructor + * @extends {module:crypto-algorithms/base.EncryptionAlgorithm} + * + * @param {string} deviceId The identifier for this device. + * @param {module:crypto} crypto crypto core + * @param {module:OlmDevice} olmDevice olm.js wrapper + * + */ +function OlmEncryption(deviceId, crypto, olmDevice) { + base.EncryptionAlgorithm.call(this, deviceId, crypto, olmDevice); +} +utils.inherits(OlmEncryption, 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 + */ +OlmEncryption.prototype.initRoomEncryption = function(roomMembers) { + var crypto = this._crypto; + return crypto.downloadKeys(roomMembers, true).then(function(res) { + return crypto.ensureOlmSessionsForUsers(roomMembers); + }); +}; + +/** + * @inheritdoc + * + * @param {module:models/room} room + * @param {string} eventType + * @param {object} plaintext event content + * + * @return {object} 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 + // just as you are sending a secret message? + + var users = utils.map(room.getJoinedMembers(), function(u) { + return u.userId; + }); + + var participantKeys = []; + for (var i = 0; i < users.length; ++i) { + var userId = users[i]; + var devices = this._crypto.getStoredDevicesForUser(userId); + for (var j = 0; j < devices.length; ++j) { + var dev = devices[j]; + if (dev.blocked) { + continue; + } + + for (var keyId in dev.keys) { + if (keyId.indexOf("curve25519:") === 0) { + participantKeys.push(dev.keys[keyId]); + } + } + } + } + participantKeys.sort(); + var participantHash = ""; // Olm.sha256(participantKeys.join()); + var payloadJson = { + room_id: room.roomId, + type: eventType, + fingerprint: participantHash, + sender_device: this._deviceId, + content: content + }; + var ciphertext = {}; + var payloadString = JSON.stringify(payloadJson); + for (i = 0; i < participantKeys.length; ++i) { + var deviceKey = participantKeys[i]; + if (deviceKey == this._olmDevice.deviceCurve25519Key) { + continue; + } + var sessionIds = this._olmDevice.getSessionIdsForDevice(deviceKey); + // Use the session with the lowest ID. + sessionIds.sort(); + if (sessionIds.length === 0) { + // If we don't have a session for a device then + // we can't encrypt a message for it. + continue; + } + var sessionId = sessionIds[0]; + console.log("Using sessionid " + sessionId + " for device " + deviceKey); + ciphertext[deviceKey] = this._olmDevice.encryptMessage( + deviceKey, sessionId, payloadString + ); + } + var encryptedContent = { + algorithm: OLM_ALGORITHM, + sender_key: this._olmDevice.deviceCurve25519Key, + ciphertext: ciphertext + }; + return encryptedContent; +}; + +/** + * Olm decryption implementation + * + * @constructor + * @extends {module:crypto-algorithms/base.DecryptionAlgorithm} + * + * @param {string} deviceId The identifier for this device. + * @param {module:crypto} crypto crypto core + * @param {module:OlmDevice} olmDevice olm.js wrapper + * + */ +function OlmDecryption(deviceId, crypto, olmDevice) { + base.DecryptionAlgorithm.call(this, deviceId, crypto, olmDevice); +} +utils.inherits(OlmDecryption, base.DecryptionAlgorithm); + +/** + * @inheritdoc + * + * @param {object} event raw event + * + * @return {object} decrypted payload (with properties 'type', 'content') + * + * @throws {module:crypto-algorithms/base.DecryptionError} if there is a + * problem decrypting the event + */ +OlmDecryption.prototype.decryptEvent = function(event) { + var content = event.content; + var deviceKey = content.sender_key; + var ciphertext = content.ciphertext; + + if (!ciphertext) { + throw new base.DecryptionError("Missing ciphertext"); + } + + if (!(this._olmDevice.deviceCurve25519Key in content.ciphertext)) { + throw new base.DecryptionError("Not included in recipients"); + } + + var message = content.ciphertext[this._olmDevice.deviceCurve25519Key]; + var sessionIds = this._olmDevice.getSessionIdsForDevice(deviceKey); + var payloadString = null; + var foundSession = false; + for (var i = 0; i < sessionIds.length; i++) { + var sessionId = sessionIds[i]; + var res = this._olmDevice.decryptMessage( + deviceKey, sessionId, message.type, message.body + ); + payloadString = res.payload; + if (payloadString) { + console.log("decrypted with sessionId " + sessionId); + break; + } + + if (res.matchesInbound) { + // this was a prekey message which matched this session; don't + // create a new session. + foundSession = true; + break; + } + } + + if (message.type === 0 && !foundSession && payloadString === null) { + try { + payloadString = this._olmDevice.createInboundSession( + deviceKey, message.type, message.body + ); + console.log("created new inbound sesion"); + } catch (e) { + // Failed to decrypt with a new session. + } + } + + // TODO: Check the sender user id matches the sender key. + // TODO: check the room_id and fingerprint + if (payloadString !== null) { + return JSON.parse(payloadString); + } else { + throw new base.DecryptionError("Bad Encrypted Message"); + } +}; + +base.registerAlgorithm(OLM_ALGORITHM, OlmEncryption, OlmDecryption); diff --git a/lib/crypto.js b/lib/crypto.js index 99eaa18f4..80923bb8f 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -23,11 +23,15 @@ limitations under the License. var anotherjson = require('another-json'); var q = require("q"); -var utils = require("./utils"); var OlmDevice = require("./OlmDevice"); +var algorithms = require("./crypto-algorithms"); + var OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2"; +/** + * @enum + */ var DeviceVerification = { VERIFIED: 1, UNVERIFIED: 0, @@ -46,7 +50,7 @@ var DeviceVerification = { * @property {Object.} keys a map from * <key type>:<id> -> <base64-encoded key>> * - * @property {DeviceVerification} verified whether the device has been + * @property {module:crypto~DeviceVerification} verified whether the device has been * verified by the user * * @property {Object} unsigned additional data from the homeserver @@ -119,6 +123,7 @@ DeviceInfo.prototype.getDisplayname = function() { /** * Cryptography bits * + * @alias module:crypto.Crypto * @constructor * @alias module:crypto * @@ -137,10 +142,10 @@ function Crypto(baseApis, sessionStore, userId, deviceId) { this._userId = userId; this._deviceId = deviceId; - this._cryptoAlgorithms = []; - this._olmDevice = new OlmDevice(sessionStore); - this._cryptoAlgorithms = [OLM_ALGORITHM]; + + // EncryptionAlgorithm instance for each room + this._roomAlgorithms = {}; // build our device keys: these will later be uploaded this._deviceKeys = {}; @@ -152,7 +157,7 @@ function Crypto(baseApis, sessionStore, userId, deviceId) { // add our own deviceinfo to the sessionstore var deviceInfo = { keys: this._deviceKeys, - algorithms: this._cryptoAlgorithms, + algorithms: [OLM_ALGORITHM], verified: DeviceVerification.VERIFIED, }; var myDevices = this._sessionStore.getEndToEndDevicesForUser( @@ -205,7 +210,7 @@ function _uploadDeviceKeys(crypto) { var deviceId = crypto._deviceId; var deviceKeys = { - algorithms: crypto._cryptoAlgorithms, + algorithms: [OLM_ALGORITHM], device_id: deviceId, keys: crypto._deviceKeys, user_id: userId, @@ -388,7 +393,6 @@ function _storeDeviceKeys(_olmDevice, userId, deviceId, userStore, deviceResult) delete deviceResult.signatures; var json = anotherjson.stringify(deviceResult); - console.log("msg:", json); try { _olmDevice.verifySignature(signKey, json, signature); } catch (e) { @@ -599,8 +603,6 @@ Crypto.prototype.isSenderKeyVerified = function(userId, algorithm, sender_key) { * @return {Object} A promise that will resolve when encryption is setup. */ Crypto.prototype.setRoomEncryption = function(roomId, config, roomMembers) { - var self = this; - // if we already have encryption in this room, we should ignore this event // (for now at least. maybe we should alert the user somehow?) var existingConfig = this._sessionStore.getEndToEndRoom(roomId); @@ -608,23 +610,24 @@ Crypto.prototype.setRoomEncryption = function(roomId, config, roomMembers) { if (JSON.stringify(existingConfig) != JSON.stringify(config)) { console.error("Ignoring m.room.encryption event which requests " + "a change of config in " + roomId); - return; + return q(); } } - if (config.algorithm !== OLM_ALGORITHM) { - throw new Error("Unknown algorithm: " + config.algorithm); + var AlgClass = algorithms.ENCRYPTION_CLASSES[config.algorithm]; + if (!AlgClass) { + throw new Error("Unable to encrypt with " + config.algorithm); } // remove spurious keys config = { - algorithm: OLM_ALGORITHM, + algorithm: config.algorithm, }; this._sessionStore.storeEndToEndRoom(roomId, config); - return self.downloadKeys(roomMembers, true).then(function(res) { - return self._ensureOlmSessionsForUsers(roomMembers); - }); + var alg = new AlgClass(this._deviceId, this, this._olmDevice); + this._roomAlgorithms[roomId] = alg; + return alg.initRoomEncryption(roomMembers); }; /** @@ -636,10 +639,8 @@ Crypto.prototype.setRoomEncryption = function(roomId, config, roomMembers) { * an object with keys missingUsers (a list of users with no known * olm devices), and missingDevices a list of olm devices with no * known one-time keys. - * - * @private */ -Crypto.prototype._ensureOlmSessionsForUsers = function(users) { +Crypto.prototype.ensureOlmSessionsForUsers = function(users) { var devicesWithoutSession = []; var userWithoutDevices = []; for (var i = 0; i < users.length; ++i) { @@ -710,7 +711,7 @@ Crypto.prototype._ensureOlmSessionsForUsers = function(users) { * @return {bool} whether encryption is enabled. */ Crypto.prototype.isRoomEncrypted = function(roomId) { - return (this._sessionStore.getEndToEndRoom(roomId) && true) || false; + return Boolean(this._roomAlgorithms[roomId]); }; @@ -718,7 +719,7 @@ Crypto.prototype.isRoomEncrypted = function(roomId) { * Encrypt an event according to the configuration of the room, if necessary. * * @param {module:models/event.MatrixEvent} event event to be sent - * @param {module:models/room.Room} room destination room + * @param {module:models/room} room destination room */ Crypto.prototype.encryptEventIfNeeded = function(event, room) { if (event.isEncrypted()) { @@ -735,112 +736,27 @@ Crypto.prototype.encryptEventIfNeeded = function(event, room) { var roomId = event.getRoomId(); - var e2eRoomInfo = this._sessionStore.getEndToEndRoom(roomId); - if (!e2eRoomInfo || !e2eRoomInfo.algorithm) { + var alg = this._roomAlgorithms[roomId]; + if (!alg) { // not encrypting messages in this room + + // check that the HS hasn't hidden the crypto event + if (this._sessionStore.getEndToEndRoom(roomId)) { + throw new Error( + "Room was previously configured to use encryption, but is " + + "no longer. Perhaps the homeserver is hiding the " + + "configuration event." + ); + } return; } - var encryptedContent = this._encryptMessage( - room, e2eRoomInfo, event.getType(), event.getContent() + var encryptedContent = alg.encryptMessage( + room, event.getType(), event.getContent() ); event.makeEncrypted("m.room.encrypted", encryptedContent); }; -/** - * - * @param {module:models/room.Room} room - * @param {object} e2eRoomInfo - * @param {string} eventType - * @param {object} content - * - * @return {object} new event body - * - * @private - */ -Crypto.prototype._encryptMessage = function(room, e2eRoomInfo, eventType, content) { - if (e2eRoomInfo.algorithm !== OLM_ALGORITHM) { - throw new Error("Unknown end-to-end algorithm: " + e2eRoomInfo.algorithm); - } - - 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 - // just as you are sending a secret message? - - var users = utils.map(room.getJoinedMembers(), function(u) { - return u.userId; - }); - - var participantKeys = []; - for (var i = 0; i < users.length; ++i) { - var userId = users[i]; - var devices = this.getStoredDevicesForUser(userId); - for (var j = 0; j < devices.length; ++j) { - var dev = devices[j]; - if (dev.blocked) { - continue; - } - - for (var keyId in dev.keys) { - if (keyId.indexOf("curve25519:") === 0) { - participantKeys.push(dev.keys[keyId]); - } - } - } - } - participantKeys.sort(); - var participantHash = ""; // Olm.sha256(participantKeys.join()); - var payloadJson = { - room_id: room.roomId, - type: eventType, - fingerprint: participantHash, - sender_device: this._deviceId, - content: content - }; - var ciphertext = {}; - var payloadString = JSON.stringify(payloadJson); - for (i = 0; i < participantKeys.length; ++i) { - var deviceKey = participantKeys[i]; - if (deviceKey == this._olmDevice.deviceCurve25519Key) { - continue; - } - var sessionIds = this._olmDevice.getSessionIdsForDevice(deviceKey); - // Use the session with the lowest ID. - sessionIds.sort(); - if (sessionIds.length === 0) { - // If we don't have a session for a device then - // we can't encrypt a message for it. - continue; - } - var sessionId = sessionIds[0]; - console.log("Using sessionid " + sessionId + " for device " + deviceKey); - ciphertext[deviceKey] = this._olmDevice.encryptMessage( - deviceKey, sessionId, payloadString - ); - } - var encryptedContent = { - algorithm: e2eRoomInfo.algorithm, - sender_key: this._olmDevice.deviceCurve25519Key, - ciphertext: ciphertext - }; - return encryptedContent; -}; - -function DecryptionError(msg) { - this.message = msg; -} -utils.inherits(DecryptionError, Error); - -/** - * Exception thrown when decryption fails - */ -Crypto.DecryptionError = DecryptionError; - /** * Decrypt a received event * @@ -848,66 +764,23 @@ Crypto.DecryptionError = DecryptionError; * * @return {object} decrypted payload (with properties 'type', 'content') * - * @raises {DecryptionError} if there is a problem decrypting the event + * @raises {algorithms.DecryptionError} if there is a problem decrypting the event */ Crypto.prototype.decryptEvent = function(event) { var content = event.content; - if (content.algorithm !== OLM_ALGORITHM) { - throw new DecryptionError("Unknown algorithm"); - } - - var deviceKey = content.sender_key; - var ciphertext = content.ciphertext; - - if (!ciphertext) { - throw new DecryptionError("Missing ciphertext"); - } - - if (!(this._olmDevice.deviceCurve25519Key in content.ciphertext)) { - throw new DecryptionError("Not included in recipients"); - } - - var message = content.ciphertext[this._olmDevice.deviceCurve25519Key]; - var sessionIds = this._olmDevice.getSessionIdsForDevice(deviceKey); - var payloadString = null; - var foundSession = false; - for (var i = 0; i < sessionIds.length; i++) { - var sessionId = sessionIds[i]; - var res = this._olmDevice.decryptMessage( - deviceKey, sessionId, message.type, message.body - ); - payloadString = res.payload; - if (payloadString) { - console.log("decrypted with sessionId " + sessionId); - break; - } - - if (res.matchesInbound) { - // this was a prekey message which matched this session; don't - // create a new session. - foundSession = true; - break; - } - } - - if (message.type === 0 && !foundSession && payloadString === null) { - try { - payloadString = this._olmDevice.createInboundSession( - deviceKey, message.type, message.body - ); - console.log("created new inbound sesion"); - } catch (e) { - // Failed to decrypt with a new session. - } - } - - // TODO: Check the sender user id matches the sender key. - if (payloadString !== null) { - return JSON.parse(payloadString); - } else { - throw new DecryptionError("Bad Encrypted Message"); + var AlgClass = algorithms.DECRYPTION_CLASSES[content.algorithm]; + if (!AlgClass) { + throw new algorithms.DecryptionError("Unable to decrypt " + content.algorithm); } + var alg = new AlgClass(this._deviceId, this, this._olmDevice); + return alg.decryptEvent(event); }; +/** + * @see module:crypto-algorithms/base.DecryptionError + */ +Crypto.DecryptionError = algorithms.DecryptionError; + + /** */ module.exports = Crypto; diff --git a/lib/matrix.js b/lib/matrix.js index 8612527e9..4a8528cd9 100644 --- a/lib/matrix.js +++ b/lib/matrix.js @@ -30,7 +30,7 @@ module.exports.MatrixHttpApi = require("./http-api").MatrixHttpApi; module.exports.MatrixError = require("./http-api").MatrixError; /** The {@link module:client.MatrixClient|MatrixClient} class. */ module.exports.MatrixClient = require("./client").MatrixClient; -/** The {@link module:models/room~Room|Room} class. */ +/** The {@link module:models/room|Room} class. */ module.exports.Room = require("./models/room"); /** The {@link module:models/event-timeline~EventTimeline} class. */ module.exports.EventTimeline = require("./models/event-timeline"); diff --git a/lib/models/room.js b/lib/models/room.js index cc8e955a5..7bf9b127f 100644 --- a/lib/models/room.js +++ b/lib/models/room.js @@ -75,6 +75,7 @@ function synthesizeReceipt(userId, event, receiptType) { * map from event_id to timeline and index. * * @constructor + * @alias module:models/room * @param {string} roomId Required. The ID of this room. * @param {Object=} opts Configuration options * @param {*} opts.storageToken Optional. The token which a data store can use @@ -85,7 +86,7 @@ function synthesizeReceipt(userId, event, receiptType) { * appear in a room's timeline. If "chronological", messages will appear * in the timeline when the call to sendEvent was made. If * "detached", pending messages will appear in a separate list, - * accessbile via {@link module:models/room~Room#getPendingEvents}. Default: + * accessbile via {@link module:models/room#getPendingEvents}. Default: * "chronological". * * @param {boolean} [opts.timelineSupport = false] Set to true to enable improved diff --git a/lib/timeline-window.js b/lib/timeline-window.js index 539429982..89acec9dc 100644 --- a/lib/timeline-window.js +++ b/lib/timeline-window.js @@ -41,7 +41,7 @@ var DEFAULT_PAGINATE_LOOP_LIMIT = 5; * Construct a TimelineWindow. * *

This abstracts the separate timelines in a Matrix {@link - * module:models/room~Room|Room} into a single iterable thing. It keeps track of + * module:models/room|Room} into a single iterable thing. It keeps track of * the start and endpoints of the window, which can be advanced with the help * of pagination requests. *