1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-11-28 05:03:59 +03:00

Use to-device events for key sharing

Synapse now supports out-of-band messages, so use them instead of sending the
key-sharing messages in-band.
This commit is contained in:
Richard van der Hoff
2016-09-07 13:56:54 +01:00
parent af0f5b37d8
commit 9c18893ae5
4 changed files with 109 additions and 79 deletions

View File

@@ -66,8 +66,9 @@ function MatrixBaseApis(opts) {
extraParams: opts.queryParams
};
this._http = new httpApi.MatrixHttpApi(this, httpOpts);
}
this._txnCtr = 0;
}
/**
* Get the Homeserver URL of this client
@@ -100,6 +101,15 @@ MatrixBaseApis.prototype.isLoggedIn = function() {
return this._http.opts.accessToken !== undefined;
};
/**
* Make up a new transaction id
*
* @return {string} a new, unique, transaction id
*/
MatrixBaseApis.prototype.makeTxnId = function() {
return "m" + new Date().getTime() + "." + (this._txnCtr++);
};
// Registration/Login operations
// =============================
@@ -970,6 +980,39 @@ MatrixBaseApis.prototype.lookupThreePid = function(medium, address, callback) {
);
};
// Direct-to-device messaging
// ==========================
/**
* Send an event to a specific list of devices
*
* @param {string} eventType type of event to send
* @param {Object.<string, Object<string, Object>>} contentMap
* content to send. Map from user_id to device_id to content object.
* @param {string=} txnId transaction id. One will be made up if not
* supplied.
* @return {module:client.Promise} Resolves to the result object
*/
MatrixBaseApis.prototype.sendToDevice = function(
eventType, contentMap, txnId
) {
var path = utils.encodeUri("/sendToDevice/$eventType/$txnId", {
$eventType: eventType,
$txnId: txnId ? txnId : this.makeTxnId(),
});
var body = {
messages: contentMap,
};
return this._http.authedRequestWithPrefix(
undefined, "PUT", path, undefined, body,
httpApi.PREFIX_UNSTABLE
);
};
/**
* MatrixBaseApis object
*/

View File

@@ -144,7 +144,6 @@ function MatrixClient(opts) {
this._peekSync = null;
this._isGuest = false;
this._ongoingScrollbacks = {};
this._txnCtr = 0;
this.timelineSupport = Boolean(opts.timelineSupport);
this.urlPreviewCache = {};
@@ -424,6 +423,12 @@ function setupCryptoEventHandler(client) {
});
client.on("RoomMember.membership",
client._crypto.onRoomMembership.bind(client._crypto));
client.on("toDeviceEvent", function(event) {
if (event.getType() == "m.room_key") {
client._crypto.onRoomKeyEvent(event);
}
});
}
function onCryptoEvent(client, event) {
@@ -438,20 +443,6 @@ function onCryptoEvent(client, event) {
}
}
/**
* handle a room key event
*
* @private
*
* @param {MatrixEvent} event
*/
MatrixClient.prototype._onRoomKeyEvent = function(event) {
if (!this._crypto) {
return;
}
this._crypto.onRoomKeyEvent(event);
};
/**
* Enable end-to-end encryption for a room.
* @param {string} roomId The room ID to enable encryption in.
@@ -820,7 +811,7 @@ MatrixClient.prototype.sendEvent = function(roomId, eventType, content, txnId,
if (utils.isFunction(txnId)) { callback = txnId; txnId = undefined; }
if (!txnId) {
txnId = "m" + new Date().getTime() + "." + (this._txnCtr++);
txnId = this.makeTxnId();
}
// we always construct a MatrixEvent when sending because the store and
@@ -921,11 +912,13 @@ function _updatePendingEventStatus(room, event, newStatus) {
}
function _sendEventHttpRequest(client, event) {
var txnId = event._txnId ? event._txnId : client.makeTxnId();
var pathParams = {
$roomId: event.getRoomId(),
$eventType: event.getWireType(),
$stateKey: event.getStateKey(),
$txnId: event._txnId ? event._txnId : new Date().getTime()
$txnId: txnId,
};
var path;
@@ -2663,13 +2656,6 @@ function _PojoToMatrixEventMapper(client) {
clearData = _decryptMessage(client, plainOldJsObject);
}
var matrixEvent = new MatrixEvent(plainOldJsObject, clearData);
// XXXX massive hack to deal with the fact that megolm keys are in the
// room for now, and we need to handle them before attempting to
// decrypt the following megolm messages.
if (matrixEvent.getType() == "m.room_key") {
client._onRoomKeyEvent(matrixEvent);
}
return matrixEvent;
}
return mapper;

View File

@@ -96,17 +96,15 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) {
).then(function(res) {
return self._crypto.ensureOlmSessionsForUsers(roomMembers);
}).then(function(devicemap) {
// TODO: send OOB messages. for now, send an in-band message. Each
// encrypted copy of the key takes up about 1K, so we'll only manage
// about 60 copies before we hit the event size limit; but ultimately the
// OOB messaging API will solve that problem for us.
var contentMap = {};
var participantKeys = [];
for (var userId in devicemap) {
if (!devicemap.hasOwnProperty(userId)) {
continue;
}
contentMap[userId] = {};
var devices = devicemap[userId];
for (var deviceId in devices) {
@@ -115,29 +113,18 @@ MegolmEncryption.prototype._ensureOutboundSession = function(room) {
}
var deviceInfo = devices[deviceId].device;
participantKeys.push(deviceInfo.getIdentityKey());
}
}
var encryptedContent = olmlib.encryptMessageForDevices(
contentMap[userId][deviceId] =
olmlib.encryptMessageForDevices(
self._deviceId,
self._olmDevice,
participantKeys,
[deviceInfo.getIdentityKey()],
payload
);
var txnId = '' + (new Date().getTime());
var path = utils.encodeUri(
"/rooms/$roomId/send/m.room.encrypted/$txnId", {
$roomId: self._roomId,
$txnId: txnId,
}
);
}
// TODO: retries
return self._baseApis._http.authedRequest(
undefined, "PUT", path, undefined, encryptedContent
);
return self._baseApis.sendToDevice("m.room.encrypted", contentMap);
}).then(function() {
if (self._discardNewSession) {
// we've had cause to reset the session_id since starting this process.

View File

@@ -496,36 +496,6 @@ SyncApi.prototype._sync = function(syncOptions) {
this._currentSyncRequest.done(function(data) {
self._syncConnectionLost = false;
// data looks like:
// {
// next_batch: $token,
// presence: { events: [] },
// rooms: {
// invite: {
// $roomid: {
// invite_state: { events: [] }
// }
// },
// join: {
// $roomid: {
// state: { events: [] },
// timeline: { events: [], prev_batch: $token, limited: true },
// ephemeral: { events: [] },
// account_data: { events: [] },
// unread_notifications: {
// highlight_count: 0,
// notification_count: 0,
// }
// }
// },
// leave: {
// $roomid: {
// state: { events: [] },
// timeline: { events: [], prev_batch: $token }
// }
// }
// }
// }
// set the sync token NOW *before* processing the events. We do this so
// if something barfs on an event we can skip it rather than constantly
@@ -585,6 +555,39 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
var client = this.client;
var self = this;
// data looks like:
// {
// next_batch: $token,
// presence: { events: [] },
// account_data: { events: [] },
// to_device: { events: [] },
// rooms: {
// invite: {
// $roomid: {
// invite_state: { events: [] }
// }
// },
// join: {
// $roomid: {
// state: { events: [] },
// timeline: { events: [], prev_batch: $token, limited: true },
// ephemeral: { events: [] },
// account_data: { events: [] },
// unread_notifications: {
// highlight_count: 0,
// notification_count: 0,
// }
// }
// },
// leave: {
// $roomid: {
// state: { events: [] },
// timeline: { events: [], prev_batch: $token }
// }
// }
// },
// }
// TODO-arch:
// - Each event we pass through needs to be emitted via 'event', can we
// do this in one place?
@@ -622,6 +625,17 @@ SyncApi.prototype._processSyncResponse = function(syncToken, data) {
);
}
// handle to-device events
if (data.to_device && utils.isArray(data.to_device.events)) {
data.to_device.events
.map(client.getEventMapper())
.forEach(
function(toDeviceEvent) {
client.emit("toDeviceEvent", toDeviceEvent);
}
);
}
// the returned json structure is a bit crap, so make it into a
// nicer form (array) after applying sanity to make sure we don't fail
// on missing keys (on the off chance)