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

Add MatrixClient.initCrypto

initialising the crypto layer needs to become asynchronous. Rather than making
`sdk.createClient` asynchronous, which would break every single app in the
world, add `initCrypto`, which will only break those attempting to do e2e (and
in a way which will fall back to only supporting unencrypted events).
This commit is contained in:
Richard van der Hoff
2017-07-18 21:17:01 +01:00
parent e2599071c5
commit d1e91cd702
6 changed files with 116 additions and 34 deletions

View File

@@ -245,21 +245,27 @@ The SDK supports end-to-end encryption via the Olm and Megolm protocols, using
[libolm](http://matrix.org/git/olm). It is left up to the application to make [libolm](http://matrix.org/git/olm). It is left up to the application to make
libolm available, via the ``Olm`` global. libolm available, via the ``Olm`` global.
If the ``Olm`` global is not available, the SDK will show a warning: It is also necessry to call ``matrixClient.initCrypto()`` after creating a new
``MatrixClient`` (but **before** calling ``matrixClient.startClient()``) to
initialise the crypto layer.
If the ``Olm`` global is not available, the SDK will show a warning, as shown
below; ``initCrypto()`` will also fail.
``` ```
Unable to load crypto module: crypto will be disabled: Error: global.Olm is not defined Unable to load crypto module: crypto will be disabled: Error: global.Olm is not defined
``` ```
The SDK will continue to work for unencrypted rooms, but it will not support If the crypto layer is not (successfully) initialised, the SDK will continue to
the E2E parts of the Matrix specification. work for unencrypted rooms, but it will not support the E2E parts of the Matrix
specification.
To enable E2E support in a browser application: To provide the Olm library in a browser application:
* download the transpiled libolm (from https://matrix.org/packages/npm/olm/). * download the transpiled libolm (from https://matrix.org/packages/npm/olm/).
* load ``olm.js`` as a ``<script>`` *before* ``browser-matrix.js``. * load ``olm.js`` as a ``<script>`` *before* ``browser-matrix.js``.
To enable E2E support in a node.js application: To provide the Olm library in a node.js application:
* ``npm install https://matrix.org/packages/npm/olm/olm-2.2.2.tgz`` * ``npm install https://matrix.org/packages/npm/olm/olm-2.2.2.tgz``
(replace the URL with the latest version you want to use from (replace the URL with the latest version you want to use from

View File

@@ -388,11 +388,14 @@ describe("MatrixClient crypto", function() {
return; return;
} }
beforeEach(function() { beforeEach(async function() {
testUtils.beforeEach(this); // eslint-disable-line no-invalid-this testUtils.beforeEach(this); // eslint-disable-line no-invalid-this
aliTestClient = new TestClient(aliUserId, aliDeviceId, aliAccessToken); aliTestClient = new TestClient(aliUserId, aliDeviceId, aliAccessToken);
await aliTestClient.client.initCrypto();
bobTestClient = new TestClient(bobUserId, bobDeviceId, bobAccessToken); bobTestClient = new TestClient(bobUserId, bobDeviceId, bobAccessToken);
await bobTestClient.client.initCrypto();
aliMessages = []; aliMessages = [];
bobMessages = []; bobMessages = [];

View File

@@ -305,6 +305,10 @@ describe("MatrixClient", function() {
return; return;
} }
beforeEach(function() {
return client.initCrypto();
});
it("should do an HTTP request and then store the keys", function(done) { it("should do an HTTP request and then store the keys", function(done) {
const ed25519key = "7wG2lzAqbjcyEkOP7O4gU7ItYcn+chKzh5sT/5r2l78"; const ed25519key = "7wG2lzAqbjcyEkOP7O4gU7ItYcn+chKzh5sT/5r2l78";
// ed25519key = client.getDeviceEd25519Key(); // ed25519key = client.getDeviceEd25519Key();

View File

@@ -280,12 +280,13 @@ describe("megolm", function() {
return claimResponse; return claimResponse;
} }
beforeEach(function() { beforeEach(async function() {
testUtils.beforeEach(this); // eslint-disable-line no-invalid-this testUtils.beforeEach(this); // eslint-disable-line no-invalid-this
aliceTestClient = new TestClient( aliceTestClient = new TestClient(
"@alice:localhost", "xzcvb", "akjgkrgjs", "@alice:localhost", "xzcvb", "akjgkrgjs",
); );
await aliceTestClient.client.initCrypto();
testOlmAccount = new Olm.Account(); testOlmAccount = new Olm.Account();
testOlmAccount.create(); testOlmAccount.create();
@@ -1079,10 +1080,10 @@ describe("megolm", function() {
aliceTestClient = new TestClient( aliceTestClient = new TestClient(
"@alice:localhost", "device2", "access_token2", "@alice:localhost", "device2", "access_token2",
); );
return aliceTestClient.client.initCrypto().then(() => {
aliceTestClient.client.importRoomKeys(exported); aliceTestClient.client.importRoomKeys(exported);
return aliceTestClient.start();
return aliceTestClient.start(); });
}).then(function() { }).then(function() {
const syncResponse = { const syncResponse = {
next_batch: 1, next_batch: 1,

View File

@@ -161,21 +161,9 @@ function MatrixClient(opts) {
this._crypto = null; this._crypto = null;
this._cryptoStore = opts.cryptoStore; this._cryptoStore = opts.cryptoStore;
if (CRYPTO_ENABLED && Boolean(opts.sessionStore) && this._sessionStore = opts.sessionStore;
Boolean(this._cryptoStore) &&
userId !== null && this.deviceId !== null) {
this._crypto = new Crypto(
this, this,
opts.sessionStore,
userId, this.deviceId,
this.store,
opts.cryptoStore,
);
reEmit(this, this._crypto, [
"crypto.roomKeyRequest",
"crypto.roomKeyRequestCancellation",
]);
if (CRYPTO_ENABLED) {
this.olmVersion = Crypto.getOlmVersion(); this.olmVersion = Crypto.getOlmVersion();
} }
} }
@@ -323,6 +311,72 @@ MatrixClient.prototype.setNotifTimelineSet = function(notifTimelineSet) {
// Crypto bits // Crypto bits
// =========== // ===========
/**
* Initialise support for end-to-end encryption in this client
*
* You should call this method after creating the matrixclient, but *before*
* calling `startClient`, if you want to support end-to-end encryption.
*
* It will return a Promise which will resolve when the crypto layer has been
* successfully initialised.
*/
MatrixClient.prototype.initCrypto = async function() {
if (this._crypto) {
console.warn("Attempt to re-initialise e2e encryption on MatrixClient");
return;
}
if (!CRYPTO_ENABLED) {
throw new Error(
`End-to-end encryption not supported in this js-sdk build: did ` +
`you remember to load the olm library?`,
);
}
if (!this._sessionStore) {
// this is temporary, the sessionstore is supposed to be going away
throw new Error(`Cannot enable encryption: no sessionStore provided`);
}
if (!this._cryptoStore) {
// the cryptostore is provided by sdk.createClient, so this shouldn't happen
throw new Error(`Cannot enable encryption: no cryptoStore provided`);
}
const userId = this.getUserId();
if (userId === null) {
throw new Error(
`Cannot enable encryption on MatrixClient with unknown userId: ` +
`ensure userId is passed in createClient().`,
);
}
if (this.deviceId === null) {
throw new Error(
`Cannot enable encryption on MatrixClient with unknown deviceId: ` +
`ensure deviceId is passed in createClient().`,
);
}
const crypto = new Crypto(
this,
this._sessionStore,
userId, this.deviceId,
this.store,
this._cryptoStore,
);
reEmit(this, crypto, [
"crypto.roomKeyRequest",
"crypto.roomKeyRequestCancellation",
]);
await crypto.init();
// if crypto initialisation was sucessful, tell it to attach its event handlers.
crypto.registerEventHandlers(this);
this._crypto = crypto;
};
/** /**
* Is end-to-end crypto enabled for this client. * Is end-to-end crypto enabled for this client.
* @return {boolean} True if end-to-end is enabled. * @return {boolean} True if end-to-end is enabled.

View File

@@ -37,13 +37,14 @@ import OutgoingRoomKeyRequestManager from './OutgoingRoomKeyRequestManager';
/** /**
* Cryptography bits * Cryptography bits
* *
* This module is internal to the js-sdk; the public API is via MatrixClient.
*
* @constructor * @constructor
* @alias module:crypto * @alias module:crypto
* *
* @param {module:base-apis~MatrixBaseApis} baseApis base matrix api interface * @internal
* *
* @param {external:EventEmitter} eventEmitter event source where we can register * @param {module:base-apis~MatrixBaseApis} baseApis base matrix api interface
* for event notifications
* *
* @param {module:store/session/webstorage~WebStorageSessionStore} sessionStore * @param {module:store/session/webstorage~WebStorageSessionStore} sessionStore
* Store to be used for end-to-end crypto session data * Store to be used for end-to-end crypto session data
@@ -57,7 +58,7 @@ import OutgoingRoomKeyRequestManager from './OutgoingRoomKeyRequestManager';
* @param {module:crypto/store/base~CryptoStore} cryptoStore * @param {module:crypto/store/base~CryptoStore} cryptoStore
* storage for the crypto layer. * storage for the crypto layer.
*/ */
function Crypto(baseApis, eventEmitter, sessionStore, userId, deviceId, function Crypto(baseApis, sessionStore, userId, deviceId,
clientStore, cryptoStore) { clientStore, cryptoStore) {
this._baseApis = baseApis; this._baseApis = baseApis;
this._sessionStore = sessionStore; this._sessionStore = sessionStore;
@@ -85,8 +86,9 @@ function Crypto(baseApis, eventEmitter, sessionStore, userId, deviceId,
algorithms.DECRYPTION_CLASSES, algorithms.DECRYPTION_CLASSES,
); );
// build our device keys: these will later be uploaded
this._deviceKeys = {}; this._deviceKeys = {};
// build our device keys: these will later be uploaded
this._deviceKeys["ed25519:" + this._deviceId] = this._deviceKeys["ed25519:" + this._deviceId] =
this._olmDevice.deviceEd25519Key; this._olmDevice.deviceEd25519Key;
this._deviceKeys["curve25519:" + this._deviceId] = this._deviceKeys["curve25519:" + this._deviceId] =
@@ -125,13 +127,24 @@ function Crypto(baseApis, eventEmitter, sessionStore, userId, deviceId,
this._userId, myDevices, this._userId, myDevices,
); );
} }
_registerEventHandlers(this, eventEmitter);
} }
utils.inherits(Crypto, EventEmitter); utils.inherits(Crypto, EventEmitter);
/**
* Initialise the crypto module so that it is ready for use
*/
Crypto.prototype.init = async function() {
};
function _registerEventHandlers(crypto, eventEmitter) { /**
* Tell the crypto module to register for MatrixClient events which it needs to
* listen for
*
* @param {external:EventEmitter} eventEmitter event source where we can register
* for event notifications
*/
Crypto.prototype.registerEventHandlers = function(eventEmitter) {
const crypto = this;
eventEmitter.on("sync", function(syncState, oldState, data) { eventEmitter.on("sync", function(syncState, oldState, data) {
try { try {
if (syncState === "SYNCING") { if (syncState === "SYNCING") {
@@ -173,7 +186,8 @@ function _registerEventHandlers(crypto, eventEmitter) {
console.error("Error handling crypto event:", e); console.error("Error handling crypto event:", e);
} }
}); });
} };
/** Start background processes related to crypto */ /** Start background processes related to crypto */
Crypto.prototype.start = function() { Crypto.prototype.start = function() {