You've already forked matrix-js-sdk
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:
16
README.md
16
README.md
@@ -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
|
||||||
|
|||||||
@@ -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 = [];
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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.
|
||||||
|
|||||||
@@ -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() {
|
||||||
|
|||||||
Reference in New Issue
Block a user