mirror of https://github.com/vector-im/riot-web
Add a config flag to enable the rust crypto-sdk (#9759)
This PR adds an option to `config.json` which will make the js-sdk use the rust crypto sdk, instead of the libolm implementation. To use it, you need to add something like this to `config.json`: ``` "features": { "feature_rust_crypto": true }, ``` We don't (yet) have any way to migrate a device between implementations, so the setting that was in use when you log in is persisted to the device; it is *visible* via the labs section but cannot currently be changed. This is part of https://github.com/vector-im/element-web/issues/21972, and enables the functionality added to the js-sdk in https://github.com/matrix-org/matrix-js-sdk/pull/2969.pull/28217/head
parent
bfaced2172
commit
6ec6d44c96
|
@ -191,6 +191,8 @@ export interface IConfigOptions {
|
|||
description: string;
|
||||
show_once?: boolean;
|
||||
};
|
||||
|
||||
use_rust_crypto_sdk?: boolean;
|
||||
}
|
||||
|
||||
export interface ISsoRedirectOptions {
|
||||
|
|
|
@ -39,6 +39,7 @@ import SecurityCustomisations from "./customisations/Security";
|
|||
import { SlidingSyncManager } from "./SlidingSyncManager";
|
||||
import CryptoStoreTooNewDialog from "./components/views/dialogs/CryptoStoreTooNewDialog";
|
||||
import { _t } from "./languageHandler";
|
||||
import { SettingLevel } from "./settings/SettingLevel";
|
||||
|
||||
export interface IMatrixClientCreds {
|
||||
homeserverUrl: string;
|
||||
|
@ -208,24 +209,8 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||
}
|
||||
|
||||
// try to initialise e2e on the new client
|
||||
try {
|
||||
// check that we have a version of the js-sdk which includes initCrypto
|
||||
if (!SettingsStore.getValue("lowBandwidth") && this.matrixClient.initCrypto) {
|
||||
await this.matrixClient.initCrypto();
|
||||
this.matrixClient.setCryptoTrustCrossSignedDevices(
|
||||
!SettingsStore.getValue("e2ee.manuallyVerifyAllSessions"),
|
||||
);
|
||||
await tryToUnlockSecretStorageWithDehydrationKey(this.matrixClient);
|
||||
StorageManager.setCryptoInitialised(true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e && e.name === "InvalidCryptoStoreError") {
|
||||
// The js-sdk found a crypto DB too new for it to use
|
||||
Modal.createDialog(CryptoStoreTooNewDialog);
|
||||
}
|
||||
// this can happen for a number of reasons, the most likely being
|
||||
// that the olm library was missing. It's not fatal.
|
||||
logger.warn("Unable to initialise e2e", e);
|
||||
if (!SettingsStore.getValue("lowBandwidth")) {
|
||||
await this.initClientCrypto();
|
||||
}
|
||||
|
||||
const opts = utils.deepCopy(this.opts);
|
||||
|
@ -256,6 +241,48 @@ class MatrixClientPegClass implements IMatrixClientPeg {
|
|||
return opts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to initialize the crypto layer on a newly-created MatrixClient
|
||||
*/
|
||||
private async initClientCrypto(): Promise<void> {
|
||||
const useRustCrypto = SettingsStore.getValue("feature_rust_crypto");
|
||||
|
||||
// we want to make sure that the same crypto implementation is used throughout the lifetime of a device,
|
||||
// so persist the setting at the device layer
|
||||
// (At some point, we'll allow the user to *enable* the setting via labs, which will migrate their existing
|
||||
// device to the rust-sdk implementation, but that won't change anything here).
|
||||
await SettingsStore.setValue("feature_rust_crypto", null, SettingLevel.DEVICE, useRustCrypto);
|
||||
|
||||
// Now we can initialise the right crypto impl.
|
||||
if (useRustCrypto) {
|
||||
await this.matrixClient.initRustCrypto();
|
||||
|
||||
// TODO: device dehydration and whathaveyou
|
||||
return;
|
||||
}
|
||||
|
||||
// fall back to the libolm layer.
|
||||
try {
|
||||
// check that we have a version of the js-sdk which includes initCrypto
|
||||
if (this.matrixClient.initCrypto) {
|
||||
await this.matrixClient.initCrypto();
|
||||
this.matrixClient.setCryptoTrustCrossSignedDevices(
|
||||
!SettingsStore.getValue("e2ee.manuallyVerifyAllSessions"),
|
||||
);
|
||||
await tryToUnlockSecretStorageWithDehydrationKey(this.matrixClient);
|
||||
StorageManager.setCryptoInitialised(true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (e instanceof Error && e.name === "InvalidCryptoStoreError") {
|
||||
// The js-sdk found a crypto DB too new for it to use
|
||||
Modal.createDialog(CryptoStoreTooNewDialog);
|
||||
}
|
||||
// this can happen for a number of reasons, the most likely being
|
||||
// that the olm library was missing. It's not fatal.
|
||||
logger.warn("Unable to initialise e2e", e);
|
||||
}
|
||||
}
|
||||
|
||||
public async start(): Promise<any> {
|
||||
const opts = await this.assign();
|
||||
|
||||
|
|
|
@ -952,6 +952,8 @@
|
|||
"Have greater visibility and control over all your sessions.": "Have greater visibility and control over all your sessions.",
|
||||
"Our new sessions manager provides better visibility of all your sessions, and greater control over them including the ability to remotely toggle push notifications.": "Our new sessions manager provides better visibility of all your sessions, and greater control over them including the ability to remotely toggle push notifications.",
|
||||
"Allow a QR code to be shown in session manager to sign in another device (requires compatible homeserver)": "Allow a QR code to be shown in session manager to sign in another device (requires compatible homeserver)",
|
||||
"Rust cryptography implementation": "Rust cryptography implementation",
|
||||
"Under active development. Can currently only be enabled via config.json": "Under active development. Can currently only be enabled via config.json",
|
||||
"Font size": "Font size",
|
||||
"Use custom size": "Use custom size",
|
||||
"Enable Emoji suggestions while typing": "Enable Emoji suggestions while typing",
|
||||
|
|
|
@ -44,6 +44,7 @@ import SdkConfig from "../SdkConfig";
|
|||
import SlidingSyncController from "./controllers/SlidingSyncController";
|
||||
import ThreadBetaController from "./controllers/ThreadBetaController";
|
||||
import { FontWatcher } from "./watchers/FontWatcher";
|
||||
import RustCryptoSdkController from "./controllers/RustCryptoSdkController";
|
||||
|
||||
// These are just a bunch of helper arrays to avoid copy/pasting a bunch of times
|
||||
const LEVELS_ROOM_SETTINGS = [
|
||||
|
@ -491,6 +492,17 @@ export const SETTINGS: { [setting: string]: ISetting } = {
|
|||
),
|
||||
default: false,
|
||||
},
|
||||
"feature_rust_crypto": {
|
||||
// use the rust matrix-sdk-crypto-js for crypto.
|
||||
isFeature: true,
|
||||
labsGroup: LabGroup.Developer,
|
||||
supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG,
|
||||
displayName: _td("Rust cryptography implementation"),
|
||||
description: _td("Under active development. Can currently only be enabled via config.json"),
|
||||
// shouldWarn: true,
|
||||
default: false,
|
||||
controller: new RustCryptoSdkController(),
|
||||
},
|
||||
"baseFontSize": {
|
||||
displayName: _td("Font size"),
|
||||
supportedLevels: LEVELS_ACCOUNT_SETTINGS,
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
import SettingController from "./SettingController";
|
||||
|
||||
export default class RustCryptoSdkController extends SettingController {
|
||||
public get settingDisabled(): boolean {
|
||||
// Currently this can only be changed via config.json. In future, we'll allow the user to *enable* this setting
|
||||
// via labs, which will migrate their existing device to the rust-sdk implementation.
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -14,8 +14,11 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
import { logger } from "matrix-js-sdk/src/logger";
|
||||
|
||||
import { advanceDateAndTime, stubClient } from "./test-utils";
|
||||
import { MatrixClientPeg as peg } from "../src/MatrixClientPeg";
|
||||
import { IMatrixClientPeg, MatrixClientPeg as peg } from "../src/MatrixClientPeg";
|
||||
import SettingsStore from "../src/settings/SettingsStore";
|
||||
|
||||
jest.useFakeTimers();
|
||||
|
||||
|
@ -57,4 +60,71 @@ describe("MatrixClientPeg", () => {
|
|||
expect(peg.userRegisteredWithinLastHours(1)).toBe(false);
|
||||
expect(peg.userRegisteredWithinLastHours(24)).toBe(false);
|
||||
});
|
||||
|
||||
describe(".start", () => {
|
||||
let testPeg: IMatrixClientPeg;
|
||||
|
||||
beforeEach(() => {
|
||||
// instantiate a MatrixClientPegClass instance, with a new MatrixClient
|
||||
const PegClass = Object.getPrototypeOf(peg).constructor;
|
||||
testPeg = new PegClass();
|
||||
testPeg.replaceUsingCreds({
|
||||
accessToken: "SEKRET",
|
||||
homeserverUrl: "http://example.com",
|
||||
userId: "@user:example.com",
|
||||
deviceId: "TEST_DEVICE_ID",
|
||||
});
|
||||
|
||||
// stub out Logger.log which gets called a lot and clutters up the test output
|
||||
jest.spyOn(logger, "log").mockImplementation(() => {});
|
||||
});
|
||||
|
||||
it("should initialise client crypto", async () => {
|
||||
const mockInitCrypto = jest.spyOn(testPeg.get(), "initCrypto").mockResolvedValue(undefined);
|
||||
const mockSetTrustCrossSignedDevices = jest
|
||||
.spyOn(testPeg.get(), "setCryptoTrustCrossSignedDevices")
|
||||
.mockImplementation(() => {});
|
||||
const mockStartClient = jest.spyOn(testPeg.get(), "startClient").mockResolvedValue(undefined);
|
||||
|
||||
await testPeg.start();
|
||||
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
|
||||
expect(mockSetTrustCrossSignedDevices).toHaveBeenCalledTimes(1);
|
||||
expect(mockStartClient).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should carry on regardless if there is an error initialising crypto", async () => {
|
||||
const e2eError = new Error("nope nope nope");
|
||||
const mockInitCrypto = jest.spyOn(testPeg.get(), "initCrypto").mockRejectedValue(e2eError);
|
||||
const mockSetTrustCrossSignedDevices = jest
|
||||
.spyOn(testPeg.get(), "setCryptoTrustCrossSignedDevices")
|
||||
.mockImplementation(() => {});
|
||||
const mockStartClient = jest.spyOn(testPeg.get(), "startClient").mockResolvedValue(undefined);
|
||||
const mockWarning = jest.spyOn(logger, "warn").mockReturnValue(undefined);
|
||||
|
||||
await testPeg.start();
|
||||
expect(mockInitCrypto).toHaveBeenCalledTimes(1);
|
||||
expect(mockSetTrustCrossSignedDevices).not.toHaveBeenCalled();
|
||||
expect(mockStartClient).toHaveBeenCalledTimes(1);
|
||||
expect(mockWarning).toHaveBeenCalledWith(expect.stringMatching("Unable to initialise e2e"), e2eError);
|
||||
});
|
||||
|
||||
it("should initialise the rust crypto library, if enabled", async () => {
|
||||
const originalGetValue = SettingsStore.getValue;
|
||||
jest.spyOn(SettingsStore, "getValue").mockImplementation(
|
||||
(settingName: string, roomId: string | null = null, excludeDefault = false) => {
|
||||
if (settingName === "feature_rust_crypto") {
|
||||
return true;
|
||||
}
|
||||
return originalGetValue(settingName, roomId, excludeDefault);
|
||||
},
|
||||
);
|
||||
|
||||
const mockInitCrypto = jest.spyOn(testPeg.get(), "initCrypto").mockResolvedValue(undefined);
|
||||
const mockInitRustCrypto = jest.spyOn(testPeg.get(), "initRustCrypto").mockResolvedValue(undefined);
|
||||
|
||||
await testPeg.start();
|
||||
expect(mockInitCrypto).not.toHaveBeenCalled();
|
||||
expect(mockInitRustCrypto).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue