summaryrefslogtreecommitdiff
path: root/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto
diff options
context:
space:
mode:
authorRaindropsSys <contact@minteck.org>2023-04-24 14:03:36 +0200
committerRaindropsSys <contact@minteck.org>2023-04-24 14:03:36 +0200
commit633c92eae865e957121e08de634aeee11a8b3992 (patch)
tree09d881bee1dae0b6eee49db1dfaf0f500240606c /includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto
parentc4657e4509733699c0f26a3c900bab47e915d5a0 (diff)
downloadpluralconnect-633c92eae865e957121e08de634aeee11a8b3992.tar.gz
pluralconnect-633c92eae865e957121e08de634aeee11a8b3992.tar.bz2
pluralconnect-633c92eae865e957121e08de634aeee11a8b3992.zip
Updated 18 files, added 1692 files and deleted includes/system/compare.inc (automated)
Diffstat (limited to 'includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto')
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/KeyClaimManager.ts77
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/OutgoingRequestProcessor.ts116
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/RoomEncryptor.ts153
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/browserify-index.ts31
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/constants.ts18
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/index.ts45
-rw-r--r--includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/rust-crypto.ts334
7 files changed, 774 insertions, 0 deletions
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/KeyClaimManager.ts b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/KeyClaimManager.ts
new file mode 100644
index 0000000..9df8f89
--- /dev/null
+++ b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/KeyClaimManager.ts
@@ -0,0 +1,77 @@
+/*
+Copyright 2023 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 { OlmMachine, UserId } from "@matrix-org/matrix-sdk-crypto-js";
+
+import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
+
+/**
+ * KeyClaimManager: linearises calls to OlmMachine.getMissingSessions to avoid races
+ *
+ * We have one of these per `RustCrypto` (and hence per `MatrixClient`).
+ */
+export class KeyClaimManager {
+ private currentClaimPromise: Promise<void>;
+ private stopped = false;
+
+ public constructor(
+ private readonly olmMachine: OlmMachine,
+ private readonly outgoingRequestProcessor: OutgoingRequestProcessor,
+ ) {
+ this.currentClaimPromise = Promise.resolve();
+ }
+
+ /**
+ * Tell the KeyClaimManager to immediately stop processing requests.
+ *
+ * Any further calls, and any still in the queue, will fail with an error.
+ */
+ public stop(): void {
+ this.stopped = true;
+ }
+
+ /**
+ * Given a list of users, attempt to ensure that we have Olm Sessions active with each of their devices
+ *
+ * If we don't have an active olm session, we will claim a one-time key and start one.
+ *
+ * @param userList - list of userIDs to claim
+ */
+ public ensureSessionsForUsers(userList: Array<UserId>): Promise<void> {
+ // The Rust-SDK requires that we only have one getMissingSessions process in flight at once. This little dance
+ // ensures that, by only having one call to ensureSessionsForUsersInner active at once (and making them
+ // queue up in order).
+ const prom = this.currentClaimPromise
+ .catch(() => {
+ // any errors in the previous claim will have been reported already, so there is nothing to do here.
+ // we just throw away the error and start anew.
+ })
+ .then(() => this.ensureSessionsForUsersInner(userList));
+ this.currentClaimPromise = prom;
+ return prom;
+ }
+
+ private async ensureSessionsForUsersInner(userList: Array<UserId>): Promise<void> {
+ // bail out quickly if we've been stopped.
+ if (this.stopped) {
+ throw new Error(`Cannot ensure Olm sessions: shutting down`);
+ }
+ const claimRequest = await this.olmMachine.getMissingSessions(userList);
+ if (claimRequest) {
+ await this.outgoingRequestProcessor.makeOutgoingRequest(claimRequest);
+ }
+ }
+}
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/OutgoingRequestProcessor.ts b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/OutgoingRequestProcessor.ts
new file mode 100644
index 0000000..7ac9a21
--- /dev/null
+++ b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/OutgoingRequestProcessor.ts
@@ -0,0 +1,116 @@
+/*
+Copyright 2023 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 {
+ OlmMachine,
+ KeysBackupRequest,
+ KeysClaimRequest,
+ KeysQueryRequest,
+ KeysUploadRequest,
+ RoomMessageRequest,
+ SignatureUploadRequest,
+ ToDeviceRequest,
+} from "@matrix-org/matrix-sdk-crypto-js";
+
+import { logger } from "../logger";
+import { IHttpOpts, MatrixHttpApi, Method } from "../http-api";
+import { QueryDict } from "../utils";
+
+/**
+ * Common interface for all the request types returned by `OlmMachine.outgoingRequests`.
+ */
+export interface OutgoingRequest {
+ readonly id: string | undefined;
+ readonly type: number;
+}
+
+/**
+ * OutgoingRequestManager: turns `OutgoingRequest`s from the rust sdk into HTTP requests
+ *
+ * We have one of these per `RustCrypto` (and hence per `MatrixClient`), not that it does anything terribly complicated.
+ * It's responsible for:
+ *
+ * * holding the reference to the `MatrixHttpApi`
+ * * turning `OutgoingRequest`s from the rust backend into HTTP requests, and sending them
+ * * sending the results of such requests back to the rust backend.
+ */
+export class OutgoingRequestProcessor {
+ public constructor(
+ private readonly olmMachine: OlmMachine,
+ private readonly http: MatrixHttpApi<IHttpOpts & { onlyData: true }>,
+ ) {}
+
+ public async makeOutgoingRequest(msg: OutgoingRequest): Promise<void> {
+ let resp: string;
+
+ /* refer https://docs.rs/matrix-sdk-crypto/0.6.0/matrix_sdk_crypto/requests/enum.OutgoingRequests.html
+ * for the complete list of request types
+ */
+ if (msg instanceof KeysUploadRequest) {
+ resp = await this.rawJsonRequest(Method.Post, "/_matrix/client/v3/keys/upload", {}, msg.body);
+ } else if (msg instanceof KeysQueryRequest) {
+ resp = await this.rawJsonRequest(Method.Post, "/_matrix/client/v3/keys/query", {}, msg.body);
+ } else if (msg instanceof KeysClaimRequest) {
+ resp = await this.rawJsonRequest(Method.Post, "/_matrix/client/v3/keys/claim", {}, msg.body);
+ } else if (msg instanceof SignatureUploadRequest) {
+ resp = await this.rawJsonRequest(Method.Post, "/_matrix/client/v3/keys/signatures/upload", {}, msg.body);
+ } else if (msg instanceof KeysBackupRequest) {
+ resp = await this.rawJsonRequest(Method.Put, "/_matrix/client/v3/room_keys/keys", {}, msg.body);
+ } else if (msg instanceof ToDeviceRequest) {
+ const path =
+ `/_matrix/client/v3/sendToDevice/${encodeURIComponent(msg.event_type)}/` +
+ encodeURIComponent(msg.txn_id);
+ resp = await this.rawJsonRequest(Method.Put, path, {}, msg.body);
+ } else if (msg instanceof RoomMessageRequest) {
+ const path =
+ `/_matrix/client/v3/room/${encodeURIComponent(msg.room_id)}/send/` +
+ `${encodeURIComponent(msg.event_type)}/${encodeURIComponent(msg.txn_id)}`;
+ resp = await this.rawJsonRequest(Method.Put, path, {}, msg.body);
+ } else {
+ logger.warn("Unsupported outgoing message", Object.getPrototypeOf(msg));
+ resp = "";
+ }
+
+ if (msg.id) {
+ await this.olmMachine.markRequestAsSent(msg.id, msg.type, resp);
+ }
+ }
+
+ private async rawJsonRequest(method: Method, path: string, queryParams: QueryDict, body: string): Promise<string> {
+ const opts = {
+ // inhibit the JSON stringification and parsing within HttpApi.
+ json: false,
+
+ // nevertheless, we are sending, and accept, JSON.
+ headers: {
+ "Content-Type": "application/json",
+ "Accept": "application/json",
+ },
+
+ // we use the full prefix
+ prefix: "",
+ };
+
+ try {
+ const response = await this.http.authedRequest<string>(method, path, queryParams, body, opts);
+ logger.info(`rust-crypto: successfully made HTTP request: ${method} ${path}`);
+ return response;
+ } catch (e) {
+ logger.warn(`rust-crypto: error making HTTP request: ${method} ${path}: ${e}`);
+ throw e;
+ }
+ }
+}
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/RoomEncryptor.ts b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/RoomEncryptor.ts
new file mode 100644
index 0000000..1649a69
--- /dev/null
+++ b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/RoomEncryptor.ts
@@ -0,0 +1,153 @@
+/*
+Copyright 2023 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 { EncryptionSettings, OlmMachine, RoomId, UserId } from "@matrix-org/matrix-sdk-crypto-js";
+
+import { EventType } from "../@types/event";
+import { IContent, MatrixEvent } from "../models/event";
+import { Room } from "../models/room";
+import { logger, PrefixedLogger } from "../logger";
+import { KeyClaimManager } from "./KeyClaimManager";
+import { RoomMember } from "../models/room-member";
+import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
+
+/**
+ * RoomEncryptor: responsible for encrypting messages to a given room
+ */
+export class RoomEncryptor {
+ private readonly prefixedLogger: PrefixedLogger;
+
+ /**
+ * @param olmMachine - The rust-sdk's OlmMachine
+ * @param keyClaimManager - Our KeyClaimManager, which manages the queue of one-time-key claim requests
+ * @param room - The room we want to encrypt for
+ * @param encryptionSettings - body of the m.room.encryption event currently in force in this room
+ */
+ public constructor(
+ private readonly olmMachine: OlmMachine,
+ private readonly keyClaimManager: KeyClaimManager,
+ private readonly outgoingRequestProcessor: OutgoingRequestProcessor,
+ private readonly room: Room,
+ private encryptionSettings: IContent,
+ ) {
+ this.prefixedLogger = logger.withPrefix(`[${room.roomId} encryption]`);
+ }
+
+ /**
+ * Handle a new `m.room.encryption` event in this room
+ *
+ * @param config - The content of the encryption event
+ */
+ public onCryptoEvent(config: IContent): void {
+ if (JSON.stringify(this.encryptionSettings) != JSON.stringify(config)) {
+ this.prefixedLogger.error(`Ignoring m.room.encryption event which requests a change of config`);
+ }
+ }
+
+ /**
+ * Handle a new `m.room.member` event in this room
+ *
+ * @param member - new membership state
+ */
+ public onRoomMembership(member: RoomMember): void {
+ this.prefixedLogger.debug(`${member.membership} event for ${member.userId}`);
+
+ if (
+ member.membership == "join" ||
+ (member.membership == "invite" && this.room.shouldEncryptForInvitedMembers())
+ ) {
+ // make sure we are tracking the deviceList for this user
+ this.prefixedLogger.debug(`starting to track devices for: ${member.userId}`);
+ this.olmMachine.updateTrackedUsers([new UserId(member.userId)]);
+ }
+
+ // TODO: handle leaves (including our own)
+ }
+
+ /**
+ * Prepare to encrypt events in this room.
+ *
+ * This ensures that we have a megolm session ready to use and that we have shared its key with all the devices
+ * in the room.
+ */
+ public async ensureEncryptionSession(): Promise<void> {
+ if (this.encryptionSettings.algorithm !== "m.megolm.v1.aes-sha2") {
+ throw new Error(
+ `Cannot encrypt in ${this.room.roomId} for unsupported algorithm '${this.encryptionSettings.algorithm}'`,
+ );
+ }
+
+ const members = await this.room.getEncryptionTargetMembers();
+ this.prefixedLogger.debug(
+ `Encrypting for users (shouldEncryptForInvitedMembers: ${this.room.shouldEncryptForInvitedMembers()}):`,
+ members.map((u) => `${u.userId} (${u.membership})`),
+ );
+
+ const userList = members.map((u) => new UserId(u.userId));
+ await this.keyClaimManager.ensureSessionsForUsers(userList);
+
+ this.prefixedLogger.debug("Sessions for users are ready; now sharing room key");
+
+ const rustEncryptionSettings = new EncryptionSettings();
+ /* FIXME historyVisibility, rotation, etc */
+
+ const shareMessages = await this.olmMachine.shareRoomKey(
+ new RoomId(this.room.roomId),
+ userList,
+ rustEncryptionSettings,
+ );
+ if (shareMessages) {
+ for (const m of shareMessages) {
+ await this.outgoingRequestProcessor.makeOutgoingRequest(m);
+ }
+ }
+ }
+
+ /**
+ * Discard any existing group session for this room
+ */
+ public async forceDiscardSession(): Promise<void> {
+ const r = await this.olmMachine.invalidateGroupSession(new RoomId(this.room.roomId));
+ if (r) {
+ this.prefixedLogger.info("Discarded existing group session");
+ }
+ }
+
+ /**
+ * Encrypt an event for this room
+ *
+ * This will ensure that we have a megolm session for this room, share it with the devices in the room, and
+ * then encrypt the event using the session.
+ *
+ * @param event - Event to be encrypted.
+ */
+ public async encryptEvent(event: MatrixEvent): Promise<void> {
+ await this.ensureEncryptionSession();
+
+ const encryptedContent = await this.olmMachine.encryptRoomEvent(
+ new RoomId(this.room.roomId),
+ event.getType(),
+ JSON.stringify(event.getContent()),
+ );
+
+ event.makeEncrypted(
+ EventType.RoomMessageEncrypted,
+ JSON.parse(encryptedContent),
+ this.olmMachine.identityKeys.curve25519.toBase64(),
+ this.olmMachine.identityKeys.ed25519.toBase64(),
+ );
+ }
+}
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/browserify-index.ts b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/browserify-index.ts
new file mode 100644
index 0000000..7f91e90
--- /dev/null
+++ b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/browserify-index.ts
@@ -0,0 +1,31 @@
+/*
+Copyright 2023 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.
+*/
+
+/* This file replaces rust-crypto/index.ts when the js-sdk is being built for browserify.
+ *
+ * It is a stub, so that we do not import the whole of the base64'ed wasm artifact into the browserify bundle.
+ * It deliberately does nothing except raise an exception.
+ */
+
+import { IHttpOpts, MatrixHttpApi } from "../http-api";
+
+export async function initRustCrypto(
+ _http: MatrixHttpApi<IHttpOpts & { onlyData: true }>,
+ _userId: string,
+ _deviceId: string,
+): Promise<Crypto> {
+ throw new Error("Rust crypto is not supported under browserify.");
+}
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/constants.ts b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/constants.ts
new file mode 100644
index 0000000..9d72060
--- /dev/null
+++ b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/constants.ts
@@ -0,0 +1,18 @@
+/*
+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.
+*/
+
+/** The prefix used on indexeddbs created by rust-crypto */
+export const RUST_SDK_STORE_PREFIX = "matrix-js-sdk";
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/index.ts b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/index.ts
new file mode 100644
index 0000000..e2c541f
--- /dev/null
+++ b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/index.ts
@@ -0,0 +1,45 @@
+/*
+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 * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-js";
+
+import { RustCrypto } from "./rust-crypto";
+import { logger } from "../logger";
+import { RUST_SDK_STORE_PREFIX } from "./constants";
+import { IHttpOpts, MatrixHttpApi } from "../http-api";
+
+export async function initRustCrypto(
+ http: MatrixHttpApi<IHttpOpts & { onlyData: true }>,
+ userId: string,
+ deviceId: string,
+): Promise<RustCrypto> {
+ // initialise the rust matrix-sdk-crypto-js, if it hasn't already been done
+ await RustSdkCryptoJs.initAsync();
+
+ // enable tracing in the rust-sdk
+ new RustSdkCryptoJs.Tracing(RustSdkCryptoJs.LoggerLevel.Trace).turnOn();
+
+ const u = new RustSdkCryptoJs.UserId(userId);
+ const d = new RustSdkCryptoJs.DeviceId(deviceId);
+ logger.info("Init OlmMachine");
+
+ // TODO: use the pickle key for the passphrase
+ const olmMachine = await RustSdkCryptoJs.OlmMachine.initialize(u, d, RUST_SDK_STORE_PREFIX, "test pass");
+ const rustCrypto = new RustCrypto(olmMachine, http, userId, deviceId);
+
+ logger.info("Completed rust crypto-sdk setup");
+ return rustCrypto;
+}
diff --git a/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/rust-crypto.ts b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/rust-crypto.ts
new file mode 100644
index 0000000..4a0b1f8
--- /dev/null
+++ b/includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto/rust-crypto.ts
@@ -0,0 +1,334 @@
+/*
+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 * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-js";
+
+import type { IEventDecryptionResult, IMegolmSessionData } from "../@types/crypto";
+import type { IToDeviceEvent } from "../sync-accumulator";
+import type { IEncryptedEventInfo } from "../crypto/api";
+import { MatrixEvent } from "../models/event";
+import { Room } from "../models/room";
+import { RoomMember } from "../models/room-member";
+import { CryptoBackend, OnSyncCompletedData } from "../common-crypto/CryptoBackend";
+import { logger } from "../logger";
+import { IHttpOpts, MatrixHttpApi } from "../http-api";
+import { DeviceTrustLevel, UserTrustLevel } from "../crypto/CrossSigning";
+import { RoomEncryptor } from "./RoomEncryptor";
+import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
+import { KeyClaimManager } from "./KeyClaimManager";
+
+/**
+ * An implementation of {@link CryptoBackend} using the Rust matrix-sdk-crypto.
+ */
+export class RustCrypto implements CryptoBackend {
+ public globalErrorOnUnknownDevices = false;
+
+ /** whether {@link stop} has been called */
+ private stopped = false;
+
+ /** whether {@link outgoingRequestLoop} is currently running */
+ private outgoingRequestLoopRunning = false;
+
+ /** mapping of roomId → encryptor class */
+ private roomEncryptors: Record<string, RoomEncryptor> = {};
+
+ private keyClaimManager: KeyClaimManager;
+ private outgoingRequestProcessor: OutgoingRequestProcessor;
+
+ public constructor(
+ private readonly olmMachine: RustSdkCryptoJs.OlmMachine,
+ http: MatrixHttpApi<IHttpOpts & { onlyData: true }>,
+ _userId: string,
+ _deviceId: string,
+ ) {
+ this.outgoingRequestProcessor = new OutgoingRequestProcessor(olmMachine, http);
+ this.keyClaimManager = new KeyClaimManager(olmMachine, this.outgoingRequestProcessor);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // CryptoBackend implementation
+ //
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public stop(): void {
+ // stop() may be called multiple times, but attempting to close() the OlmMachine twice
+ // will cause an error.
+ if (this.stopped) {
+ return;
+ }
+ this.stopped = true;
+
+ this.keyClaimManager.stop();
+
+ // make sure we close() the OlmMachine; doing so means that all the Rust objects will be
+ // cleaned up; in particular, the indexeddb connections will be closed, which means they
+ // can then be deleted.
+ this.olmMachine.close();
+ }
+
+ public async encryptEvent(event: MatrixEvent, _room: Room): Promise<void> {
+ const roomId = event.getRoomId()!;
+ const encryptor = this.roomEncryptors[roomId];
+
+ if (!encryptor) {
+ throw new Error(`Cannot encrypt event in unconfigured room ${roomId}`);
+ }
+
+ await encryptor.encryptEvent(event);
+ }
+
+ public async decryptEvent(event: MatrixEvent): Promise<IEventDecryptionResult> {
+ const roomId = event.getRoomId();
+ if (!roomId) {
+ // presumably, a to-device message. These are normally decrypted in preprocessToDeviceMessages
+ // so the fact it has come back here suggests that decryption failed.
+ //
+ // once we drop support for the libolm crypto implementation, we can stop passing to-device messages
+ // through decryptEvent and hence get rid of this case.
+ throw new Error("to-device event was not decrypted in preprocessToDeviceMessages");
+ }
+ const res = (await this.olmMachine.decryptRoomEvent(
+ JSON.stringify({
+ event_id: event.getId(),
+ type: event.getWireType(),
+ sender: event.getSender(),
+ state_key: event.getStateKey(),
+ content: event.getWireContent(),
+ origin_server_ts: event.getTs(),
+ }),
+ new RustSdkCryptoJs.RoomId(event.getRoomId()!),
+ )) as RustSdkCryptoJs.DecryptedRoomEvent;
+ return {
+ clearEvent: JSON.parse(res.event),
+ claimedEd25519Key: res.senderClaimedEd25519Key,
+ senderCurve25519Key: res.senderCurve25519Key,
+ forwardingCurve25519KeyChain: res.forwardingCurve25519KeyChain,
+ };
+ }
+
+ public getEventEncryptionInfo(event: MatrixEvent): IEncryptedEventInfo {
+ // TODO: make this work properly. Or better, replace it.
+
+ const ret: Partial<IEncryptedEventInfo> = {};
+
+ ret.senderKey = event.getSenderKey() ?? undefined;
+ ret.algorithm = event.getWireContent().algorithm;
+
+ if (!ret.senderKey || !ret.algorithm) {
+ ret.encrypted = false;
+ return ret as IEncryptedEventInfo;
+ }
+ ret.encrypted = true;
+ ret.authenticated = true;
+ ret.mismatchedSender = true;
+ return ret as IEncryptedEventInfo;
+ }
+
+ public checkUserTrust(userId: string): UserTrustLevel {
+ // TODO
+ return new UserTrustLevel(false, false, false);
+ }
+
+ public checkDeviceTrust(userId: string, deviceId: string): DeviceTrustLevel {
+ // TODO
+ return new DeviceTrustLevel(false, false, false, false);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // CryptoApi implementation
+ //
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ public globalBlacklistUnverifiedDevices = false;
+
+ public async userHasCrossSigningKeys(): Promise<boolean> {
+ // TODO
+ return false;
+ }
+
+ public prepareToEncrypt(room: Room): void {
+ const encryptor = this.roomEncryptors[room.roomId];
+
+ if (encryptor) {
+ encryptor.ensureEncryptionSession();
+ }
+ }
+
+ public forceDiscardSession(roomId: string): Promise<void> {
+ return this.roomEncryptors[roomId]?.forceDiscardSession();
+ }
+
+ public async exportRoomKeys(): Promise<IMegolmSessionData[]> {
+ // TODO
+ return [];
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // SyncCryptoCallbacks implementation
+ //
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Apply sync changes to the olm machine
+ * @param events - the received to-device messages
+ * @param oneTimeKeysCounts - the received one time key counts
+ * @param unusedFallbackKeys - the received unused fallback keys
+ * @returns A list of preprocessed to-device messages.
+ */
+ private async receiveSyncChanges({
+ events,
+ oneTimeKeysCounts = new Map<string, number>(),
+ unusedFallbackKeys = new Set<string>(),
+ }: {
+ events?: IToDeviceEvent[];
+ oneTimeKeysCounts?: Map<string, number>;
+ unusedFallbackKeys?: Set<string>;
+ }): Promise<IToDeviceEvent[]> {
+ const result = await this.olmMachine.receiveSyncChanges(
+ events ? JSON.stringify(events) : "[]",
+ new RustSdkCryptoJs.DeviceLists(),
+ oneTimeKeysCounts,
+ unusedFallbackKeys,
+ );
+
+ // receiveSyncChanges returns a JSON-encoded list of decrypted to-device messages.
+ return JSON.parse(result);
+ }
+
+ /** called by the sync loop to preprocess incoming to-device messages
+ *
+ * @param events - the received to-device messages
+ * @returns A list of preprocessed to-device messages.
+ */
+ public preprocessToDeviceMessages(events: IToDeviceEvent[]): Promise<IToDeviceEvent[]> {
+ // send the received to-device messages into receiveSyncChanges. We have no info on device-list changes,
+ // one-time-keys, or fallback keys, so just pass empty data.
+ return this.receiveSyncChanges({ events });
+ }
+
+ /** called by the sync loop to preprocess one time key counts
+ *
+ * @param oneTimeKeysCounts - the received one time key counts
+ * @returns A list of preprocessed to-device messages.
+ */
+ public async preprocessOneTimeKeyCounts(oneTimeKeysCounts: Map<string, number>): Promise<void> {
+ await this.receiveSyncChanges({ oneTimeKeysCounts });
+ }
+
+ /** called by the sync loop to preprocess unused fallback keys
+ *
+ * @param unusedFallbackKeys - the received unused fallback keys
+ * @returns A list of preprocessed to-device messages.
+ */
+ public async preprocessUnusedFallbackKeys(unusedFallbackKeys: Set<string>): Promise<void> {
+ await this.receiveSyncChanges({ unusedFallbackKeys });
+ }
+
+ /** called by the sync loop on m.room.encrypted events
+ *
+ * @param room - in which the event was received
+ * @param event - encryption event to be processed
+ */
+ public async onCryptoEvent(room: Room, event: MatrixEvent): Promise<void> {
+ const config = event.getContent();
+
+ const existingEncryptor = this.roomEncryptors[room.roomId];
+ if (existingEncryptor) {
+ existingEncryptor.onCryptoEvent(config);
+ } else {
+ this.roomEncryptors[room.roomId] = new RoomEncryptor(
+ this.olmMachine,
+ this.keyClaimManager,
+ this.outgoingRequestProcessor,
+ room,
+ config,
+ );
+ }
+
+ // start tracking devices for any users already known to be in this room.
+ const members = await room.getEncryptionTargetMembers();
+ logger.debug(
+ `[${room.roomId} encryption] starting to track devices for: `,
+ members.map((u) => `${u.userId} (${u.membership})`),
+ );
+ await this.olmMachine.updateTrackedUsers(members.map((u) => new RustSdkCryptoJs.UserId(u.userId)));
+ }
+
+ /** called by the sync loop after processing each sync.
+ *
+ * TODO: figure out something equivalent for sliding sync.
+ *
+ * @param syncState - information on the completed sync.
+ */
+ public onSyncCompleted(syncState: OnSyncCompletedData): void {
+ // Processing the /sync may have produced new outgoing requests which need sending, so kick off the outgoing
+ // request loop, if it's not already running.
+ this.outgoingRequestLoop();
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Other public functions
+ //
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ /** called by the MatrixClient on a room membership event
+ *
+ * @param event - The matrix event which caused this event to fire.
+ * @param member - The member whose RoomMember.membership changed.
+ * @param oldMembership - The previous membership state. Null if it's a new member.
+ */
+ public onRoomMembership(event: MatrixEvent, member: RoomMember, oldMembership?: string): void {
+ const enc = this.roomEncryptors[event.getRoomId()!];
+ if (!enc) {
+ // not encrypting in this room
+ return;
+ }
+ enc.onRoomMembership(member);
+ }
+
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ //
+ // Outgoing requests
+ //
+ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+ private async outgoingRequestLoop(): Promise<void> {
+ if (this.outgoingRequestLoopRunning) {
+ return;
+ }
+ this.outgoingRequestLoopRunning = true;
+ try {
+ while (!this.stopped) {
+ const outgoingRequests: Object[] = await this.olmMachine.outgoingRequests();
+ if (outgoingRequests.length == 0 || this.stopped) {
+ // no more messages to send (or we have been told to stop): exit the loop
+ return;
+ }
+ for (const msg of outgoingRequests) {
+ await this.outgoingRequestProcessor.makeOutgoingRequest(msg as OutgoingRequest);
+ }
+ }
+ } catch (e) {
+ logger.error("Error processing outgoing-message requests from rust crypto-sdk", e);
+ } finally {
+ this.outgoingRequestLoopRunning = false;
+ }
+ }
+}