diff options
author | RaindropsSys <contact@minteck.org> | 2023-04-24 14:03:36 +0200 |
---|---|---|
committer | RaindropsSys <contact@minteck.org> | 2023-04-24 14:03:36 +0200 |
commit | 633c92eae865e957121e08de634aeee11a8b3992 (patch) | |
tree | 09d881bee1dae0b6eee49db1dfaf0f500240606c /includes/external/matrix/node_modules/matrix-js-sdk/src/rust-crypto | |
parent | c4657e4509733699c0f26a3c900bab47e915d5a0 (diff) | |
download | pluralconnect-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')
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; + } + } +} |