1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-29 16:43:09 +03:00

Reset megolm session when people join/leave the room

This commit is contained in:
Richard van der Hoff
2016-08-26 11:23:01 +01:00
parent 2da70ca024
commit b4f22310ea
7 changed files with 91 additions and 14 deletions

View File

@@ -403,6 +403,8 @@ function setupCryptoEventHandler(client) {
} }
onCryptoEvent(client, event); onCryptoEvent(client, event);
}); });
client.on("RoomMember.membership",
client._crypto.onRoomMembership.bind(client._crypto));
} }
function onCryptoEvent(client, event) { function onCryptoEvent(client, event) {

View File

@@ -90,6 +90,13 @@ EncryptionAlgorithm.prototype.initRoomEncryption = function(roomMembers) {
* @return {module:client.Promise} Promise which resolves to the new event body * @return {module:client.Promise} Promise which resolves to the new event body
*/ */
/**
* Called when the membership of a member of the room changes.
*
* @param {module:models/event.MatrixEvent} event event causing the change
* @param {module:models/room-member} member user whose membership changed
*/
EncryptionAlgorithm.prototype.onRoomMembership = function(event, member) {};
/** /**
* base type for decryption implementations * base type for decryption implementations
@@ -125,7 +132,7 @@ module.exports.DecryptionAlgorithm = DecryptionAlgorithm;
* *
* @method module:crypto-algorithms/base.DecryptionAlgorithm#onRoomKeyEvent * @method module:crypto-algorithms/base.DecryptionAlgorithm#onRoomKeyEvent
* *
* @param {module:modules/event~MatrixEvent} event key event * @param {module:models/event.MatrixEvent} event key event
*/ */
DecryptionAlgorithm.prototype.onRoomKeyEvent = function(params) { DecryptionAlgorithm.prototype.onRoomKeyEvent = function(params) {
// ignore by default // ignore by default

View File

@@ -40,6 +40,7 @@ function MegolmEncryption(params) {
base.EncryptionAlgorithm.call(this, params); base.EncryptionAlgorithm.call(this, params);
this._prepPromise = null; this._prepPromise = null;
this._outboundSessionId = null; this._outboundSessionId = null;
this._discardNewSession = false;
} }
utils.inherits(MegolmEncryption, base.EncryptionAlgorithm); utils.inherits(MegolmEncryption, base.EncryptionAlgorithm);
@@ -59,7 +60,7 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) {
if (this._outboundSessionId) { if (this._outboundSessionId) {
// prep already done // prep already done
return q(); return q(this._outboundSessionId);
} }
var session_id = this._olmDevice.createOutboundGroupSession(); var session_id = this._olmDevice.createOutboundGroupSession();
@@ -138,11 +139,19 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) {
undefined, "PUT", path, undefined, encryptedContent undefined, "PUT", path, undefined, encryptedContent
); );
}).then(function() { }).then(function() {
// don't set this until the keys are sent successfully; if we get an if (self._discardNewSession) {
// error, the user can restart by resending the message. // we've had cause to reset the session_id since starting this process.
// we'll use the current session for any currently pending events, but
// don't save it as the current _outboundSessionId, so that new events
// will use a new session.
console.log("Session generation complete, but discarding");
} else {
self._outboundSessionId = session_id; self._outboundSessionId = session_id;
}
return session_id;
}).finally(function() { }).finally(function() {
self._prepPromise = null; self._prepPromise = null;
self._discardNewSession = false;
}); });
return this._prepPromise; return this._prepPromise;
@@ -159,7 +168,7 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) {
*/ */
MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) { MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
var self = this; var self = this;
return this._ensureOutboundSession(room).then(function() { return this._ensureOutboundSession(room).then(function(session_id) {
var payloadJson = { var payloadJson = {
room_id: self._roomId, room_id: self._roomId,
type: eventType, type: eventType,
@@ -167,14 +176,14 @@ MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
}; };
var ciphertext = self._olmDevice.encryptGroupMessage( var ciphertext = self._olmDevice.encryptGroupMessage(
self._outboundSessionId, JSON.stringify(payloadJson) session_id, JSON.stringify(payloadJson)
); );
var encryptedContent = { var encryptedContent = {
algorithm: olmlib.MEGOLM_ALGORITHM, algorithm: olmlib.MEGOLM_ALGORITHM,
sender_key: self._olmDevice.deviceCurve25519Key, sender_key: self._olmDevice.deviceCurve25519Key,
body: ciphertext, body: ciphertext,
session_id: self._outboundSessionId, session_id: session_id,
signature: "FIXME", signature: "FIXME",
}; };
@@ -182,6 +191,36 @@ MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
}); });
}; };
/**
* @inheritdoc
*
* @param {module:models/event.MatrixEvent} event event causing the change
* @param {module:models/room-member} member user whose membership changed
*/
MegolmEncryption.prototype.onRoomMembership = function(event, member) {
// start a new outbound session whenever someone joins or leaves the room.
//
// technically we don't need to reset on all membership transitions (eg,
// leave->ban), but we might as well.
// when people join the room, we could get away with sharing the current
// state of the ratchet with them; however, it's somewhat easier for now
// just to reset the session and start a new one.
if (this._outboundSessionId) {
console.log("Discarding outbound megolm session due to change in " +
"membership of " + member.userId);
this._outboundSessionId = null;
}
if (this._prepPromise) {
console.log("Discarding as-yet-incomplete megolm session due to " +
"change in membership of " + member.userId);
this._discardNewSession = true;
}
};
/** /**
* Megolm decryption implementation * Megolm decryption implementation
* *
@@ -231,7 +270,7 @@ MegolmDecryption.prototype.decryptEvent = function(event) {
/** /**
* @inheritdoc * @inheritdoc
* *
* @param {module:modules/event~MatrixEvent} event key event * @param {module:models/event.MatrixEvent} event key event
*/ */
MegolmDecryption.prototype.onRoomKeyEvent = function(event) { MegolmDecryption.prototype.onRoomKeyEvent = function(event) {
console.log("Adding key from ", event); console.log("Adding key from ", event);

View File

@@ -732,7 +732,7 @@ Crypto.prototype.decryptEvent = function(event) {
/** /**
* Handle a key event * Handle a key event
* *
* @param {module:modules/event~MatrixEvent} event key event * @param {module:models/event.MatrixEvent} event key event
*/ */
Crypto.prototype.onRoomKeyEvent = function(event) { Crypto.prototype.onRoomKeyEvent = function(event) {
var content = event.getContent(); var content = event.getContent();
@@ -748,6 +748,32 @@ Crypto.prototype.onRoomKeyEvent = function(event) {
alg.onRoomKeyEvent(event); alg.onRoomKeyEvent(event);
}; };
/**
* Handle a change in the membership state of a member of a room
*
* @param {module:models/event.MatrixEvent} event event causing the change
* @param {module:models/room-member} member user whose membership changed
*/
Crypto.prototype.onRoomMembership = function(event, member) {
// this event handler is registered on the *client* (as opposed to the
// room member itself), which means it is only called on changes to the
// *live* membership state (ie, it is not called when we back-paginate).
//
// Further, it is automatically registered and called when new members
// arrive in the room.
var roomId = member.roomId;
var alg = this._roomAlgorithms[roomId];
if (!alg) {
// not encrypting in this room
return;
}
alg.onRoomMembership(event, member);
};
/** /**
* @see module:crypto-algorithms/base.DecryptionError * @see module:crypto-algorithms/base.DecryptionError
*/ */

View File

@@ -34,7 +34,7 @@ module.exports.MatrixClient = require("./client").MatrixClient;
module.exports.Room = require("./models/room"); module.exports.Room = require("./models/room");
/** The {@link module:models/event-timeline~EventTimeline} class. */ /** The {@link module:models/event-timeline~EventTimeline} class. */
module.exports.EventTimeline = require("./models/event-timeline"); module.exports.EventTimeline = require("./models/event-timeline");
/** The {@link module:models/room-member~RoomMember|RoomMember} class. */ /** The {@link module:models/room-member|RoomMember} class. */
module.exports.RoomMember = require("./models/room-member"); module.exports.RoomMember = require("./models/room-member");
/** The {@link module:models/room-state~RoomState|RoomState} class. */ /** The {@link module:models/room-state~RoomState|RoomState} class. */
module.exports.RoomState = require("./models/room-state"); module.exports.RoomState = require("./models/room-state");

View File

@@ -24,7 +24,10 @@ var utils = require("../utils");
/** /**
* Construct a new room member. * Construct a new room member.
*
* @constructor * @constructor
* @alias module:models/room-member
*
* @param {string} roomId The room ID of the member. * @param {string} roomId The room ID of the member.
* @param {string} userId The user ID of the member. * @param {string} userId The user ID of the member.
* @prop {string} roomId The room ID for this member. * @prop {string} roomId The room ID for this member.

View File

@@ -275,7 +275,7 @@ Room.prototype.getTimelineForEvent = function(eventId) {
* Get an event which is stored in our timelines * Get an event which is stored in our timelines
* *
* @param {string} eventId event ID to look for * @param {string} eventId event ID to look for
* @return {?module:models/event~MatrixEvent} the given event, or undefined if unknown * @return {?module:models/event.MatrixEvent} the given event, or undefined if unknown
*/ */
Room.prototype.findEventById = function(eventId) { Room.prototype.findEventById = function(eventId) {
var tl = this.getTimelineForEvent(eventId); var tl = this.getTimelineForEvent(eventId);
@@ -796,9 +796,9 @@ Room.prototype.addPendingEvent = function(event, txnId) {
* <p>We move the event to the live timeline if it isn't there already, and * <p>We move the event to the live timeline if it isn't there already, and
* update it. * update it.
* *
* @param {module:models/event~MatrixEvent} remoteEvent The event received from * @param {module:models/event.MatrixEvent} remoteEvent The event received from
* /sync * /sync
* @param {module:models/event~MatrixEvent} localEvent The local echo, which * @param {module:models/event.MatrixEvent} localEvent The local echo, which
* should be either in the _pendingEventList or the timeline. * should be either in the _pendingEventList or the timeline.
* *
* @fires module:client~MatrixClient#event:"Room.localEchoUpdated" * @fires module:client~MatrixClient#event:"Room.localEchoUpdated"