You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
Factor out OlmDevice from client.js
MatrixClient contains quite a lot of boilerplate for manipulating the Olm things, which quite nicely factors out to a separate object.
This commit is contained in:
329
lib/OlmDevice.js
Normal file
329
lib/OlmDevice.js
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
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";
|
||||
|
||||
var Olm = require("olm");
|
||||
var utils = require("./utils");
|
||||
|
||||
/**
|
||||
* Manages the olm cryptography functions. Each OlmDevice has a single
|
||||
* OlmAccount and a number of OlmSessions.
|
||||
*
|
||||
* Accounts and sessions are kept pickled in a sessionStore.
|
||||
*
|
||||
* @constructor
|
||||
* @param {Object} sessionStore A store to be used for data in end-to-end
|
||||
* crypto
|
||||
*
|
||||
* @property {string} deviceCurve25519Key Curve25519 key for the account
|
||||
* @property {string} deviceEd25519Key Ed25519 key for the account
|
||||
*/
|
||||
function OlmDevice(sessionStore) {
|
||||
this._sessionStore = sessionStore;
|
||||
this._pickleKey = "DEFAULT_KEY";
|
||||
|
||||
var e2eKeys;
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
var e2eAccount = this._sessionStore.getEndToEndAccount();
|
||||
if (e2eAccount === null) {
|
||||
account.create();
|
||||
var pickled = account.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndAccount(pickled);
|
||||
} else {
|
||||
account.unpickle(this._pickleKey, e2eAccount);
|
||||
}
|
||||
e2eKeys = JSON.parse(account.identity_keys());
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
|
||||
this.deviceCurve25519Key = e2eKeys.curve25519;
|
||||
this.deviceEd25519Key = e2eKeys.ed25519;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Signs a message with the ed25519 key for this account.
|
||||
*
|
||||
* @param {string} message message to be signed
|
||||
* @return {string} base64-encoded signature
|
||||
*/
|
||||
OlmDevice.prototype.sign = function(message) {
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
var pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
account.unpickle(this._pickleKey, pickledAccount);
|
||||
return account.sign(message);
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current (unused, unpublished) one-time keys for this account.
|
||||
*
|
||||
* @return {object} one time keys; an object with the single property
|
||||
* <tt>curve25519<tt>, which is itself an object mapping key id to Curve25519
|
||||
* key.
|
||||
*/
|
||||
OlmDevice.prototype.getOneTimeKeys = function() {
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
var pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
account.unpickle(this._pickleKey, pickledAccount);
|
||||
return JSON.parse(account.one_time_keys());
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get the maximum number of one-time keys we can store.
|
||||
*
|
||||
* @return {number} number of keys
|
||||
*/
|
||||
OlmDevice.prototype.maxNumberOfOneTimeKeys = function() {
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
var pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
account.unpickle(this._pickleKey, pickledAccount);
|
||||
return account.max_number_of_one_time_keys();
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Marks all of the one-time keys as published.
|
||||
*/
|
||||
OlmDevice.prototype.markKeysAsPublished = function() {
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
var pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
account.unpickle(this._pickleKey, pickledAccount);
|
||||
account.mark_keys_as_published();
|
||||
pickledAccount = account.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndAccount(pickledAccount);
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate some new one-time keys
|
||||
*
|
||||
* @param {number} numKeys number of keys to generate
|
||||
*/
|
||||
OlmDevice.prototype.generateOneTimeKeys = function(numKeys) {
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
var pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
account.unpickle(this._pickleKey, pickledAccount);
|
||||
account.generate_one_time_keys(numKeys);
|
||||
pickledAccount = account.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndAccount(pickledAccount);
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a new outbound session
|
||||
*
|
||||
* The new session will be stored in the sessionStore.
|
||||
*
|
||||
* @param {string} theirIdentityKey remote user's Curve25519 identity key
|
||||
* @param {string} theirOneTimeKey remote user's one-time Curve25519 key
|
||||
* @return {string} sessionId for the outbound session.
|
||||
*/
|
||||
OlmDevice.prototype.createOutboundSession = function(
|
||||
theirIdentityKey, theirOneTimeKey
|
||||
) {
|
||||
var account = new Olm.Account();
|
||||
var session = new Olm.Session();
|
||||
try {
|
||||
var pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
account.unpickle(this._pickleKey, pickledAccount);
|
||||
|
||||
session.create_outbound(account, theirIdentityKey, theirOneTimeKey);
|
||||
|
||||
var pickledSession = session.pickle(this._pickleKey);
|
||||
var sessionId = session.session_id();
|
||||
this._sessionStore.storeEndToEndSession(
|
||||
theirIdentityKey, sessionId, pickledSession
|
||||
);
|
||||
return sessionId;
|
||||
} finally {
|
||||
session.free();
|
||||
account.free();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Generate a new inbound session, given an incoming message
|
||||
*
|
||||
* @param {string} theirDeviceIdentityKey remote user's Curve25519 identity key
|
||||
* @param {number} message_type message_type field from the received message (must be 0)
|
||||
* @param {string} ciphertext base64-encoded body from the received message
|
||||
*
|
||||
* @return {string} decrypted payload
|
||||
*
|
||||
* @raises {Error} if the received message was not valid (for instance, it
|
||||
* didn't use a valid one-time key).
|
||||
*/
|
||||
OlmDevice.prototype.createInboundSession = function(
|
||||
theirDeviceIdentityKey, message_type, ciphertext
|
||||
) {
|
||||
if (message_type !== 0) {
|
||||
throw new Error("Need message_type == 0 to create inbound session");
|
||||
}
|
||||
|
||||
var account = new Olm.Account();
|
||||
var session = new Olm.Session();
|
||||
try {
|
||||
var pickledAccount = this._sessionStore.getEndToEndAccount();
|
||||
account.unpickle(this._pickleKey, pickledAccount);
|
||||
|
||||
session.create_inbound_from(account, theirDeviceIdentityKey, ciphertext);
|
||||
account.remove_one_time_keys(session);
|
||||
|
||||
pickledAccount = account.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndAccount(pickledAccount);
|
||||
|
||||
var payloadString = session.decrypt(message_type, ciphertext);
|
||||
|
||||
var sessionId = session.session_id();
|
||||
var pickledSession = session.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndSession(
|
||||
theirDeviceIdentityKey, sessionId, pickledSession
|
||||
);
|
||||
|
||||
return payloadString;
|
||||
} finally {
|
||||
session.free();
|
||||
account.free();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Get a list of known session IDs for the given device
|
||||
*
|
||||
* @param {string} theirDeviceIdentityKey Curve25519 identity key for the
|
||||
* remote device
|
||||
* @return {string[]} a list of known session ids for the device
|
||||
*/
|
||||
OlmDevice.prototype.getSessionIdsForDevice = function(theirDeviceIdentityKey) {
|
||||
var sessions = this._sessionStore.getEndToEndSessions(
|
||||
theirDeviceIdentityKey
|
||||
);
|
||||
return utils.keys(sessions);
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Encrypt an outgoing message using an existing session
|
||||
*
|
||||
* @param {string} theirDeviceIdentityKey Curve25519 identity key for the
|
||||
* remote device
|
||||
* @param {string} sessionId the id of the active session
|
||||
* @param {string} payloadString payload to be encrypted and sent
|
||||
*
|
||||
* @return {string} ciphertext
|
||||
*/
|
||||
OlmDevice.prototype.encryptMessage = function(
|
||||
theirDeviceIdentityKey, sessionId, payloadString
|
||||
) {
|
||||
var sessions = this._sessionStore.getEndToEndSessions(
|
||||
theirDeviceIdentityKey
|
||||
);
|
||||
var pickledSession = sessions[sessionId];
|
||||
|
||||
var session = new Olm.Session();
|
||||
|
||||
try {
|
||||
session.unpickle(this._pickleKey, pickledSession);
|
||||
var res = session.encrypt(payloadString);
|
||||
pickledSession = session.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndSession(
|
||||
theirDeviceIdentityKey, sessionId, pickledSession
|
||||
);
|
||||
return res;
|
||||
} finally {
|
||||
session.free();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Decrypt an incoming message using an existing session
|
||||
*
|
||||
* @param {string} theirDeviceIdentityKey Curve25519 identity key for the
|
||||
* remote device
|
||||
* @param {string} sessionId the id of the active session
|
||||
* @param {number} message_type message_type field from the received message
|
||||
* @param {string} ciphertext base64-encoded body from the received message
|
||||
*
|
||||
* @return {string} decrypted payload
|
||||
*
|
||||
* @raises {Error} if the received message was not valid (for instance, it
|
||||
* did not match this session).
|
||||
*/
|
||||
OlmDevice.prototype.decryptMessage = function(
|
||||
theirDeviceIdentityKey, sessionId, message_type, ciphertext
|
||||
) {
|
||||
var sessions = this._sessionStore.getEndToEndSessions(
|
||||
theirDeviceIdentityKey
|
||||
);
|
||||
var pickledSession = sessions[sessionId];
|
||||
|
||||
var session = new Olm.Session();
|
||||
try {
|
||||
session.unpickle(this._pickleKey, pickledSession);
|
||||
var matchesInbound = message_type === 0 && session.matches_inbound(ciphertext);
|
||||
var payloadString = null;
|
||||
try {
|
||||
payloadString = session.decrypt(message_type, ciphertext);
|
||||
} catch (e) {
|
||||
console.log(
|
||||
"Failed to decrypt with an existing session: " + e.message
|
||||
);
|
||||
|
||||
return {
|
||||
matchesInbound: matchesInbound,
|
||||
payload: null,
|
||||
};
|
||||
}
|
||||
|
||||
// successfully decrypted: update the session
|
||||
pickledSession = session.pickle(this._pickleKey);
|
||||
this._sessionStore.storeEndToEndSession(
|
||||
theirDeviceIdentityKey, sessionId, pickledSession
|
||||
);
|
||||
|
||||
return {
|
||||
matchesInbound: matchesInbound,
|
||||
payload: payloadString,
|
||||
};
|
||||
} finally {
|
||||
session.free();
|
||||
}
|
||||
};
|
||||
|
||||
/** */
|
||||
module.exports = OlmDevice;
|
||||
207
lib/client.js
207
lib/client.js
@@ -41,10 +41,8 @@ var SCROLLBACK_DELAY_MS = 3000;
|
||||
var CRYPTO_ENABLED = false;
|
||||
|
||||
try {
|
||||
var Olm = require("olm");
|
||||
if (Olm.Account && Olm.Session) {
|
||||
var OlmDevice = require("./OlmDevice");
|
||||
CRYPTO_ENABLED = true;
|
||||
}
|
||||
} catch (e) {
|
||||
// Olm not installed.
|
||||
}
|
||||
@@ -69,10 +67,18 @@ var OLM_ALGORITHM = "m.olm.v1.curve25519-aes-sha2";
|
||||
* {@link requestFunction} for more information.
|
||||
*
|
||||
* @param {string} opts.accessToken The access_token for this user.
|
||||
*
|
||||
* @param {string} opts.userId The user ID for this user.
|
||||
* @param {Object} opts.store Optional. The data store to use. If not specified,
|
||||
*
|
||||
* @param {Object=} opts.store The data store to use. If not specified,
|
||||
* this client will not store any HTTP responses.
|
||||
*
|
||||
* @param {string=} opts.deviceId A unique identifier for this device, for use
|
||||
* in end-to-end crypto. If not specified, end-to-end crypto will be disabled.
|
||||
*
|
||||
* @param {Object=} opts.sessionStore A store to be used for end-to-end crypto
|
||||
* session data. If not specified, end-to-end crypto will be disabled.
|
||||
*
|
||||
* @param {Object} opts.scheduler Optional. The scheduler to use. If not
|
||||
* specified, this client will not retry requests on failure. This client
|
||||
* will supply its own processing function to
|
||||
@@ -97,37 +103,30 @@ function MatrixClient(opts) {
|
||||
|
||||
this.store = opts.store || new StubStore();
|
||||
this.sessionStore = opts.sessionStore || null;
|
||||
this.accountKey = "DEFAULT_KEY";
|
||||
|
||||
this.deviceId = opts.deviceId;
|
||||
this._olmDevice = null;
|
||||
|
||||
if (CRYPTO_ENABLED && this.sessionStore !== null) {
|
||||
var e2eAccount = this.sessionStore.getEndToEndAccount();
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
if (e2eAccount === null) {
|
||||
account.create();
|
||||
} else {
|
||||
account.unpickle(this.accountKey, e2eAccount);
|
||||
}
|
||||
var e2eKeys = JSON.parse(account.identity_keys());
|
||||
this._olmDevice = new OlmDevice(opts.sessionStore);
|
||||
|
||||
var json = '{"algorithms":["' + OLM_ALGORITHM + '"]';
|
||||
json += ',"device_id":"' + this.deviceId + '"';
|
||||
json += ',"keys":';
|
||||
json += '{"ed25519:' + this.deviceId + '":';
|
||||
json += JSON.stringify(e2eKeys.ed25519);
|
||||
json += JSON.stringify(this._olmDevice.deviceEd25519Key);
|
||||
json += ',"curve25519:' + this.deviceId + '":';
|
||||
json += JSON.stringify(e2eKeys.curve25519);
|
||||
json += JSON.stringify(this._olmDevice.deviceCurve25519Key);
|
||||
json += '}';
|
||||
json += ',"user_id":' + JSON.stringify(opts.userId);
|
||||
json += '}';
|
||||
var signature = account.sign(json);
|
||||
var signature = this._olmDevice.sign(json);
|
||||
this.deviceKeys = JSON.parse(json);
|
||||
var signatures = {};
|
||||
signatures[opts.userId] = {};
|
||||
signatures[opts.userId]["ed25519:" + this.deviceId] = signature;
|
||||
this.deviceKeys.signatures = signatures;
|
||||
this.deviceCurve25519Key = e2eKeys.curve25519;
|
||||
var pickled = account.pickle(this.accountKey);
|
||||
this.sessionStore.storeEndToEndAccount(pickled);
|
||||
|
||||
var myDevices = this.sessionStore.getEndToEndDevicesForUser(
|
||||
opts.userId
|
||||
) || {};
|
||||
@@ -135,9 +134,6 @@ function MatrixClient(opts) {
|
||||
this.sessionStore.storeEndToEndDevicesForUser(
|
||||
opts.userId, myDevices
|
||||
);
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
}
|
||||
this.scheduler = opts.scheduler;
|
||||
if (this.scheduler) {
|
||||
@@ -311,54 +307,29 @@ MatrixClient.prototype.uploadKeys = function(maxKeys, deferred) {
|
||||
var self = this;
|
||||
return _doKeyUpload(this).then(function(res) {
|
||||
var keyCount = res.one_time_key_counts.curve25519 || 0;
|
||||
|
||||
var pickled = self.sessionStore.getEndToEndAccount();
|
||||
|
||||
var numberToGenerate;
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
account.unpickle(self.accountKey, pickled);
|
||||
|
||||
var maxOneTimeKeys = account.max_number_of_one_time_keys();
|
||||
var maxOneTimeKeys = self._olmDevice.maxNumberOfOneTimeKeys();
|
||||
var keyLimit = Math.floor(maxOneTimeKeys / 2);
|
||||
numberToGenerate = Math.max(keyLimit - keyCount, 0);
|
||||
var numberToGenerate = Math.max(keyLimit - keyCount, 0);
|
||||
if (maxKeys !== undefined) {
|
||||
numberToGenerate = Math.min(numberToGenerate, maxKeys);
|
||||
}
|
||||
if (numberToGenerate > 0) {
|
||||
account.generate_one_time_keys(numberToGenerate);
|
||||
}
|
||||
pickled = account.pickle(self.accountKey);
|
||||
self.sessionStore.storeEndToEndAccount(pickled);
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
if (numberToGenerate > 0) {
|
||||
return _doKeyUpload(self);
|
||||
} else {
|
||||
|
||||
if (numberToGenerate <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
self._olmDevice.generateOneTimeKeys(numberToGenerate);
|
||||
return _doKeyUpload(self);
|
||||
});
|
||||
};
|
||||
|
||||
// build the upload request, and return a promise which resolves to the response
|
||||
function _doKeyUpload(client) {
|
||||
if (!CRYPTO_ENABLED || client.sessionStore === null) {
|
||||
if (!client._olmDevice) {
|
||||
return q.reject(new Error("End-to-end encryption disabled"));
|
||||
}
|
||||
|
||||
var pickled = client.sessionStore.getEndToEndAccount();
|
||||
if (!pickled) {
|
||||
return q.reject(new Error("End-to-end account not found"));
|
||||
}
|
||||
var account = new Olm.Account();
|
||||
var oneTimeKeys;
|
||||
try {
|
||||
account.unpickle(client.accountKey, pickled);
|
||||
oneTimeKeys = JSON.parse(account.one_time_keys());
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
var oneTimeKeys = client._olmDevice.getOneTimeKeys();
|
||||
var oneTimeJson = {};
|
||||
|
||||
for (var keyId in oneTimeKeys.curve25519) {
|
||||
@@ -374,15 +345,7 @@ function _doKeyUpload(client) {
|
||||
return client._http.authedRequestWithPrefix(
|
||||
undefined, "POST", path, undefined, content, httpApi.PREFIX_UNSTABLE
|
||||
).then(function(res) {
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
account.unpickle(client.accountKey, pickled);
|
||||
account.mark_keys_as_published();
|
||||
pickled = account.pickle(client.accountKey);
|
||||
client.sessionStore.storeEndToEndAccount(pickled);
|
||||
} finally {
|
||||
account.free();
|
||||
}
|
||||
client._olmDevice.markKeysAsPublished();
|
||||
return res;
|
||||
});
|
||||
}
|
||||
@@ -484,9 +447,10 @@ MatrixClient.prototype.listDeviceKeys = function(userId) {
|
||||
* @return {Object} A promise that will resolve when encryption is setup.
|
||||
*/
|
||||
MatrixClient.prototype.setRoomEncryption = function(roomId, config) {
|
||||
if (!this.sessionStore || !CRYPTO_ENABLED) {
|
||||
if (!this._olmDevice) {
|
||||
return q.reject(new Error("End-to-End encryption disabled"));
|
||||
}
|
||||
|
||||
if (config.algorithm === OLM_ALGORITHM) {
|
||||
if (!config.members) {
|
||||
throw new Error(
|
||||
@@ -505,7 +469,7 @@ MatrixClient.prototype.setRoomEncryption = function(roomId, config) {
|
||||
if (devices.hasOwnProperty(deviceId)) {
|
||||
var keys = devices[deviceId];
|
||||
var key = keys.keys["curve25519:" + deviceId];
|
||||
if (key == this.deviceCurve25519Key) {
|
||||
if (key == this._olmDevice.deviceCurve25519Key) {
|
||||
continue;
|
||||
}
|
||||
if (!this.sessionStore.getEndToEndSessions(key)) {
|
||||
@@ -543,21 +507,9 @@ MatrixClient.prototype.setRoomEncryption = function(roomId, config) {
|
||||
}
|
||||
}
|
||||
if (oneTimeKey) {
|
||||
var session = new Olm.Session();
|
||||
var account = new Olm.Account();
|
||||
try {
|
||||
var pickled = self.sessionStore.getEndToEndAccount();
|
||||
account.unpickle(self.accountKey, pickled);
|
||||
session.create_outbound(account, device[2], oneTimeKey);
|
||||
var sessionId = session.session_id();
|
||||
pickled = session.pickle(self.accountKey);
|
||||
self.sessionStore.storeEndToEndSession(
|
||||
device[2], sessionId, pickled
|
||||
self._olmDevice.createOutboundSession(
|
||||
device[2], oneTimeKey
|
||||
);
|
||||
} finally {
|
||||
session.free();
|
||||
account.free();
|
||||
}
|
||||
} else {
|
||||
missing[device[0]] = missing[device[0]] || [];
|
||||
missing[device[0]].push([device[1]]);
|
||||
@@ -1032,18 +984,10 @@ function _encryptMessage(client, roomId, e2eRoomInfo, eventType, content,
|
||||
var payloadString = JSON.stringify(payloadJson);
|
||||
for (i = 0; i < participantKeys.length; ++i) {
|
||||
var deviceKey = participantKeys[i];
|
||||
if (deviceKey == client.deviceCurve25519Key) {
|
||||
if (deviceKey == client._olmDevice.deviceCurve25519Key) {
|
||||
continue;
|
||||
}
|
||||
var sessions = client.sessionStore.getEndToEndSessions(
|
||||
deviceKey
|
||||
);
|
||||
var sessionIds = [];
|
||||
for (var sessionId in sessions) {
|
||||
if (sessions.hasOwnProperty(sessionId)) {
|
||||
sessionIds.push(sessionId);
|
||||
}
|
||||
}
|
||||
var sessionIds = client._olmDevice.getSessionIdsForDevice(deviceKey);
|
||||
// Use the session with the lowest ID.
|
||||
sessionIds.sort();
|
||||
if (sessionIds.length === 0) {
|
||||
@@ -1051,22 +995,14 @@ function _encryptMessage(client, roomId, e2eRoomInfo, eventType, content,
|
||||
// we can't encrypt a message for it.
|
||||
continue;
|
||||
}
|
||||
sessionId = sessionIds[0];
|
||||
var session = new Olm.Session();
|
||||
try {
|
||||
session.unpickle(client.accountKey, sessions[sessionId]);
|
||||
ciphertext[deviceKey] = session.encrypt(payloadString);
|
||||
var pickled = session.pickle(client.accountKey);
|
||||
client.sessionStore.storeEndToEndSession(
|
||||
deviceKey, sessionId, pickled
|
||||
var sessionId = sessionIds[0];
|
||||
ciphertext[deviceKey] = client._olmDevice.encryptMessage(
|
||||
deviceKey, sessionId, payloadString
|
||||
);
|
||||
} finally {
|
||||
session.free();
|
||||
}
|
||||
}
|
||||
var encryptedContent = {
|
||||
algorithm: e2eRoomInfo.algorithm,
|
||||
sender_key: client.deviceCurve25519Key,
|
||||
sender_key: client._olmDevice.deviceCurve25519Key,
|
||||
ciphertext: ciphertext
|
||||
};
|
||||
return encryptedContent;
|
||||
@@ -1090,59 +1026,38 @@ function _decryptMessage(client, event) {
|
||||
if (!ciphertext) {
|
||||
return _badEncryptedMessage(event, "**Missing ciphertext**");
|
||||
}
|
||||
if (!(client.deviceCurve25519Key in content.ciphertext)) {
|
||||
if (!(client._olmDevice.deviceCurve25519Key in content.ciphertext)) {
|
||||
return _badEncryptedMessage(event, "**Not included in recipients**");
|
||||
}
|
||||
var message = content.ciphertext[client.deviceCurve25519Key];
|
||||
var sessions = client.sessionStore.getEndToEndSessions(deviceKey);
|
||||
var message = content.ciphertext[client._olmDevice.deviceCurve25519Key];
|
||||
var sessionIds = client._olmDevice.getSessionIdsForDevice(deviceKey);
|
||||
var payloadString = null;
|
||||
var foundSession = false;
|
||||
var session;
|
||||
for (var sessionId in sessions) {
|
||||
if (sessions.hasOwnProperty(sessionId)) {
|
||||
session = new Olm.Session();
|
||||
try {
|
||||
session.unpickle(client.accountKey, sessions[sessionId]);
|
||||
if (message.type === 0 && session.matches_inbound(message.body)) {
|
||||
for (var i = 0; i < sessionIds.length; i++) {
|
||||
var sessionId = sessionIds[i];
|
||||
var res = client._olmDevice.decryptMessage(
|
||||
deviceKey, sessionId, message.type, message.body
|
||||
);
|
||||
payloadString = res.payload;
|
||||
if (payloadString) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (res.matchesInbound) {
|
||||
// this was a prekey message which matched this session; don't
|
||||
// create a new session.
|
||||
foundSession = true;
|
||||
}
|
||||
payloadString = session.decrypt(message.type, message.body);
|
||||
var pickled = session.pickle(client.accountKey);
|
||||
client.sessionStore.storeEndToEndSession(
|
||||
deviceKey, sessionId, pickled
|
||||
);
|
||||
} catch (e) {
|
||||
// Failed to decrypt with an existing session.
|
||||
console.log(
|
||||
"Failed to decrypt with an existing session: " + e.message
|
||||
);
|
||||
} finally {
|
||||
session.free();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (message.type === 0 && !foundSession && payloadString === null) {
|
||||
var account = new Olm.Account();
|
||||
session = new Olm.Session();
|
||||
try {
|
||||
var account_data = client.sessionStore.getEndToEndAccount();
|
||||
account.unpickle(client.accountKey, account_data);
|
||||
session.create_inbound_from(account, deviceKey, message.body);
|
||||
payloadString = session.decrypt(message.type, message.body);
|
||||
account.remove_one_time_keys(session);
|
||||
var pickledSession = session.pickle(client.accountKey);
|
||||
var pickledAccount = account.pickle(client.accountKey);
|
||||
sessionId = session.session_id();
|
||||
client.sessionStore.storeEndToEndSession(
|
||||
deviceKey, sessionId, pickledSession
|
||||
payloadString = client._olmDevice.createInboundSession(
|
||||
deviceKey, message.type, message.body
|
||||
);
|
||||
client.sessionStore.storeEndToEndAccount(pickledAccount);
|
||||
} catch (e) {
|
||||
// Failed to decrypt with a new session.
|
||||
} finally {
|
||||
session.free();
|
||||
account.free();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3093,8 +3008,8 @@ MatrixClient.prototype.startClient = function(opts) {
|
||||
|
||||
this._clientOpts = opts;
|
||||
|
||||
if (CRYPTO_ENABLED && this.sessionStore !== null) {
|
||||
this.uploadKeys(5);
|
||||
if (this._olmDevice) {
|
||||
this.uploadKeys(5).done();
|
||||
}
|
||||
|
||||
// periodically poll for turn servers if we support voip
|
||||
|
||||
@@ -88,10 +88,6 @@ describe("MatrixClient crypto", function() {
|
||||
expect(aliClient.deviceKeys.device_id).toEqual(aliDeviceId);
|
||||
done();
|
||||
});
|
||||
it("should have a curve25519 key", function(done) {
|
||||
expect(aliClient.deviceCurve25519Key).toBeDefined();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
function bobUploadsKeys() {
|
||||
|
||||
Reference in New Issue
Block a user