From 823316b2ff19fd54b129a9d1c072444dc114091a Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 26 Sep 2024 12:01:51 +0100 Subject: [PATCH 1/7] Remove use of insecure `sendSharedHistoryKeys` in MSC3089 impl --- spec/unit/models/MSC3089TreeSpace.spec.ts | 59 ++--------------------- src/models/MSC3089TreeSpace.ts | 21 ++------ 2 files changed, 7 insertions(+), 73 deletions(-) diff --git a/spec/unit/models/MSC3089TreeSpace.spec.ts b/spec/unit/models/MSC3089TreeSpace.spec.ts index 5a63c282c5a..2a07ced56cc 100644 --- a/spec/unit/models/MSC3089TreeSpace.spec.ts +++ b/spec/unit/models/MSC3089TreeSpace.spec.ts @@ -107,7 +107,7 @@ describe("MSC3089TreeSpace", () => { return Promise.resolve(); }); client.invite = fn; - await tree.invite(target, false, false); + await tree.invite(target, false); expect(fn).toHaveBeenCalledTimes(1); }); @@ -120,7 +120,7 @@ describe("MSC3089TreeSpace", () => { return Promise.resolve(); }); client.invite = fn; - await tree.invite(target, false, false); + await tree.invite(target, false); expect(fn).toHaveBeenCalledTimes(2); }); @@ -133,7 +133,7 @@ describe("MSC3089TreeSpace", () => { }); client.invite = fn; - await expect(tree.invite(target, false, false)).rejects.toThrow("MatrixError: Sample Failure"); + await expect(tree.invite(target, false)).rejects.toThrow("MatrixError: Sample Failure"); expect(fn).toHaveBeenCalledTimes(1); }); @@ -155,61 +155,10 @@ describe("MSC3089TreeSpace", () => { { invite: (userId) => fn(tree.roomId, userId) } as MSC3089TreeSpace, ]; - await tree.invite(target, true, false); + await tree.invite(target, true); expect(fn).toHaveBeenCalledTimes(4); }); - it("should share keys with invitees", async () => { - const target = targetUser; - const sendKeysFn = jest.fn().mockImplementation((inviteRoomId: string, userIds: string[]) => { - expect(inviteRoomId).toEqual(roomId); - expect(userIds).toMatchObject([target]); - return Promise.resolve(); - }); - client.invite = () => Promise.resolve({}); // we're not testing this here - see other tests - client.sendSharedHistoryKeys = sendKeysFn; - - // Mock the history check as best as possible - const historyVis = "shared"; - const historyFn = jest.fn().mockImplementation((eventType: string, stateKey?: string) => { - // We're not expecting a super rigid test: the function that calls this internally isn't - // really being tested here. - expect(eventType).toEqual(EventType.RoomHistoryVisibility); - expect(stateKey).toEqual(""); - return { getContent: () => ({ history_visibility: historyVis }) }; // eslint-disable-line camelcase - }); - room.currentState.getStateEvents = historyFn; - - // Note: inverse test is implicit from other tests, which disable the call stack of this - // test in order to pass. - await tree.invite(target, false, true); - expect(sendKeysFn).toHaveBeenCalledTimes(1); - expect(historyFn).toHaveBeenCalledTimes(1); - }); - - it("should not share keys with invitees if inappropriate history visibility", async () => { - const target = targetUser; - const sendKeysFn = jest.fn().mockImplementation((inviteRoomId: string, userIds: string[]) => { - expect(inviteRoomId).toEqual(roomId); - expect(userIds).toMatchObject([target]); - return Promise.resolve(); - }); - client.invite = () => Promise.resolve({}); // we're not testing this here - see other tests - client.sendSharedHistoryKeys = sendKeysFn; - - const historyVis = "joined"; // NOTE: Changed. - const historyFn = jest.fn().mockImplementation((eventType: string, stateKey?: string) => { - expect(eventType).toEqual(EventType.RoomHistoryVisibility); - expect(stateKey).toEqual(""); - return { getContent: () => ({ history_visibility: historyVis }) }; // eslint-disable-line camelcase - }); - room.currentState.getStateEvents = historyFn; - - await tree.invite(target, false, true); - expect(sendKeysFn).toHaveBeenCalledTimes(0); - expect(historyFn).toHaveBeenCalledTimes(1); - }); - async function evaluatePowerLevels(pls: any, role: TreePermissions, expectedPl: number) { makePowerLevels(pls); const fn = jest diff --git a/src/models/MSC3089TreeSpace.ts b/src/models/MSC3089TreeSpace.ts index 8bcc933d7c2..6de3a05fd50 100644 --- a/src/models/MSC3089TreeSpace.ts +++ b/src/models/MSC3089TreeSpace.ts @@ -30,7 +30,6 @@ import { simpleRetryOperation, } from "../utils.ts"; import { MSC3089Branch } from "./MSC3089Branch.ts"; -import { isRoomSharedHistory } from "../crypto/algorithms/megolm.ts"; import { ISendEventResponse } from "../@types/requests.ts"; import { FileType } from "../http-api/index.ts"; import { KnownMembership } from "../@types/membership.ts"; @@ -136,28 +135,14 @@ export class MSC3089TreeSpace { * @param userId - The user ID to invite. * @param andSubspaces - True (default) to invite the user to all * directories/subspaces too, recursively. - * @param shareHistoryKeys - True (default) to share encryption keys - * with the invited user. This will allow them to decrypt the events (files) - * in the tree. Keys will not be shared if the room is lacking appropriate - * history visibility (by default, history visibility is "shared" in trees, - * which is an appropriate visibility for these purposes). * @returns Promise which resolves when complete. */ - public async invite(userId: string, andSubspaces = true, shareHistoryKeys = true): Promise { + public async invite(userId: string, andSubspaces = true): Promise { const promises: Promise[] = [this.retryInvite(userId)]; if (andSubspaces) { - promises.push(...this.getDirectories().map((d) => d.invite(userId, andSubspaces, shareHistoryKeys))); + promises.push(...this.getDirectories().map((d) => d.invite(userId, andSubspaces))); } - return Promise.all(promises).then(() => { - // Note: key sharing is default on because for file trees it is relatively important that the invite - // target can actually decrypt the files. The implied use case is that by inviting a user to the tree - // it means the sender would like the receiver to view/download the files contained within, much like - // sharing a folder in other circles. - if (shareHistoryKeys && isRoomSharedHistory(this.room)) { - // noinspection JSIgnoredPromiseFromCall - we aren't concerned as much if this fails. - this.client.sendSharedHistoryKeys(this.roomId, [userId]); - } - }); + await Promise.all(promises); } private retryInvite(userId: string): Promise { From 1dcb7a6e7552471236b72dc0c420213b8b9db97f Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 26 Sep 2024 12:03:45 +0100 Subject: [PATCH 2/7] Remove insecure `MatrixClient.sendSharedHistoryKeys` method This method is impossible to use securely, and so is being removed. (It also didn't work under Rust cryptography.) In future, this functionality may be re-introduced in a safer way, but doing so will probably require updates to the MSC. --- src/client.ts | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/src/client.ts b/src/client.ts index 3a9860b3a8b..c532c460352 100644 --- a/src/client.ts +++ b/src/client.ts @@ -4087,43 +4087,6 @@ export class MatrixClient extends TypedEventEmitter { - if (!this.crypto) { - throw new Error("End-to-end encryption disabled"); - } - - const roomEncryption = this.crypto?.getRoomEncryption(roomId); - if (!roomEncryption) { - // unknown room, or unencrypted room - this.logger.error("Unknown room. Not sharing decryption keys"); - return; - } - - const deviceInfos = await this.crypto.downloadKeys(userIds); - const devicesByUser: Map = new Map(); - for (const [userId, devices] of deviceInfos) { - devicesByUser.set(userId, Array.from(devices.values())); - } - - // XXX: Private member access - const alg = this.crypto.getRoomDecryptor(roomId, roomEncryption.algorithm); - if (alg.sendSharedHistoryInboundSessions) { - await alg.sendSharedHistoryInboundSessions(devicesByUser); - } else { - this.logger.warn("Algorithm does not support sharing previous keys", roomEncryption.algorithm); - } - } - /** * Get the config for the media repository. * @returns Promise which resolves with an object containing the config. From c1b4f97372a63e4af4af3cd1bbde9705fbf506e2 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 1 Oct 2024 14:54:58 +0000 Subject: [PATCH 3/7] v34.7.0-rc.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 2b67e929dee..657cb5e6b85 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "34.6.0", + "version": "34.7.0-rc.0", "description": "Matrix Client-Server SDK for Javascript", "engines": { "node": ">=20.0.0" From 860161bdd238f33210c3f51acf16b72655540ddc Mon Sep 17 00:00:00 2001 From: Saul Date: Tue, 8 Oct 2024 23:34:01 +1300 Subject: [PATCH 4/7] Fix the rust crypto import in esm environments. (#4445) * Configure babel to fix the rust import in esm environments. * Add lockfile changes. * Cleanup rust-crypto import and babel config. --- babel.config.cjs | 17 +++++++++++++++++ package.json | 1 + src/client.ts | 4 +--- yarn.lock | 5 +++++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/babel.config.cjs b/babel.config.cjs index d5c314af157..23ab202ae02 100644 --- a/babel.config.cjs +++ b/babel.config.cjs @@ -31,5 +31,22 @@ module.exports = { "@babel/plugin-transform-object-rest-spread", "@babel/plugin-syntax-dynamic-import", "@babel/plugin-transform-runtime", + [ + "search-and-replace", + { + // Since rewriteImportExtensions doesn't work on dynamic imports (yet), we need to manually replace + // the dynamic rust-crypto import. + // (see https://github.com/babel/babel/issues/16750) + rules: + process.env.NODE_ENV !== "test" + ? [ + { + search: "./rust-crypto/index.ts", + replace: "./rust-crypto/index.js", + }, + ] + : [], + }, + ], ], }; diff --git a/package.json b/package.json index 46687405cad..e2e274b8b2f 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/parser": "^7.0.0", "babel-jest": "^29.0.0", + "babel-plugin-search-and-replace": "^1.1.1", "debug": "^4.3.4", "eslint": "8.57.0", "eslint-config-google": "^0.14.0", diff --git a/src/client.ts b/src/client.ts index 3a9860b3a8b..514586047a8 100644 --- a/src/client.ts +++ b/src/client.ts @@ -2254,9 +2254,7 @@ export class MatrixClient extends TypedEventEmitter Date: Tue, 8 Oct 2024 12:22:28 +0000 Subject: [PATCH 5/7] v34.7.0 --- CHANGELOG.md | 15 +++++++++++++++ package.json | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d0a5e1cd743..961fa8f791d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,18 @@ +Changes in [34.7.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.7.0) (2024-10-08) +================================================================================================== +## 🦖 Deprecations + +* RTCSession cleanup: deprecate getKeysForParticipant() and getEncryption(); add emitEncryptionKeys() ([#4427](https://github.com/matrix-org/matrix-js-sdk/pull/4427)). Contributed by @hughns. + +## ✨ Features + +* Bump matrix-rust-sdk to 9.1.0 ([#4435](https://github.com/matrix-org/matrix-js-sdk/pull/4435)). Contributed by @richvdh. +* Rotate Matrix RTC media encryption key when a new member joins a call for Post Compromise Security ([#4422](https://github.com/matrix-org/matrix-js-sdk/pull/4422)). Contributed by @hughns. +* Update media event content types to include captions ([#4403](https://github.com/matrix-org/matrix-js-sdk/pull/4403)). Contributed by @tulir. +* Update OIDC registration types to match latest MSC2966 state ([#4432](https://github.com/matrix-org/matrix-js-sdk/pull/4432)). Contributed by @t3chguy. +* Add `CryptoApi.pinCurrentUserIdentity` and `UserIdentity.needsUserApproval` ([#4415](https://github.com/matrix-org/matrix-js-sdk/pull/4415)). Contributed by @richvdh. + + Changes in [34.6.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.6.0) (2024-09-24) ================================================================================================== ## 🦖 Deprecations diff --git a/package.json b/package.json index 657cb5e6b85..98342b75f94 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "34.7.0-rc.0", + "version": "34.7.0", "description": "Matrix Client-Server SDK for Javascript", "engines": { "node": ">=20.0.0" From c9b43ab25120c0174e456639f2e3bccaedaded2d Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 15 Oct 2024 10:53:23 +0000 Subject: [PATCH 6/7] v34.8.0 --- CHANGELOG.md | 4 ++++ package.json | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 961fa8f791d..9b4264fc1bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +Changes in [34.8.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.8.0) (2024-10-15) +================================================================================================== +This release removes insecure functionality, resolving CVE-2024-47080 / GHSA-4jf8-g8wp-cx7c. + Changes in [34.7.0](https://github.com/matrix-org/matrix-js-sdk/releases/tag/v34.7.0) (2024-10-08) ================================================================================================== ## 🦖 Deprecations diff --git a/package.json b/package.json index 98342b75f94..9dff6ea8601 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "34.7.0", + "version": "34.8.0", "description": "Matrix Client-Server SDK for Javascript", "engines": { "node": ">=20.0.0" From 662b772c73c15e26c529827372cc038857461e89 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Tue, 15 Oct 2024 16:38:33 +0200 Subject: [PATCH 7/7] Add crypto events to `crypto-api` (#4443) * Move used Crypto event into crypto api * Use new crypto events in rust crypto * Remove `WillUpdateDevices` event from CryptoApi * Use new crypto events in old crypto events * Compute type of CryptoEvent enum * Rename CryptoEvent and CryptoEventHandlerMap as legacy * - Rename `RustCryptoEvent` as `CryptoEvent` - Declare `CryptoEventHandlerMap` into the crypto api * Add `WillUpdateDevices` back to new crypto events to avoid circular imports between old crypto and the cryto api * Extends old crypto handler map with the new crypto map * Review fixes * Add more explicit documentations --- .../PerSessionKeyBackupDownloader.spec.ts | 2 +- spec/unit/rust-crypto/backup.spec.ts | 3 +- spec/unit/rust-crypto/rust-crypto.spec.ts | 2 +- src/client.ts | 66 +++++++------ src/crypto-api/CryptoEvent.ts | 93 +++++++++++++++++++ src/crypto-api/CryptoEventHandlerMap.ts | 32 +++++++ src/crypto-api/index.ts | 2 + src/crypto/index.ts | 92 +++--------------- .../PerSessionKeyBackupDownloader.ts | 2 +- src/rust-crypto/backup.ts | 4 +- src/rust-crypto/rust-crypto.ts | 59 ++---------- 11 files changed, 190 insertions(+), 167 deletions(-) create mode 100644 src/crypto-api/CryptoEvent.ts create mode 100644 src/crypto-api/CryptoEventHandlerMap.ts diff --git a/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts b/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts index b81e17e5e5d..5a4fe4bf72a 100644 --- a/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts +++ b/spec/unit/rust-crypto/PerSessionKeyBackupDownloader.spec.ts @@ -26,7 +26,6 @@ import { RustBackupCryptoEventMap, RustBackupCryptoEvents, RustBackupManager } f import * as TestData from "../../test-utils/test-data"; import { ConnectionError, - CryptoEvent, HttpApiEvent, HttpApiEventHandlerMap, IHttpOpts, @@ -37,6 +36,7 @@ import { import * as testData from "../../test-utils/test-data"; import { BackupDecryptor } from "../../../src/common-crypto/CryptoBackend"; import { KeyBackupSession } from "../../../src/crypto-api/keybackup"; +import { CryptoEvent } from "../../../src/crypto-api/index.ts"; describe("PerSessionKeyBackupDownloader", () => { /** The downloader under test */ diff --git a/spec/unit/rust-crypto/backup.spec.ts b/spec/unit/rust-crypto/backup.spec.ts index 1a9c1435663..4b96c74f7cf 100644 --- a/spec/unit/rust-crypto/backup.spec.ts +++ b/spec/unit/rust-crypto/backup.spec.ts @@ -2,7 +2,8 @@ import { Mocked } from "jest-mock"; import fetchMock from "fetch-mock-jest"; import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; -import { CryptoEvent, HttpApiEvent, HttpApiEventHandlerMap, MatrixHttpApi, TypedEventEmitter } from "../../../src"; +import { HttpApiEvent, HttpApiEventHandlerMap, MatrixHttpApi, TypedEventEmitter } from "../../../src"; +import { CryptoEvent } from "../../../src/crypto-api/index.ts"; import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingRequestProcessor"; import * as testData from "../../test-utils/test-data"; import * as TestData from "../../test-utils/test-data"; diff --git a/spec/unit/rust-crypto/rust-crypto.spec.ts b/spec/unit/rust-crypto/rust-crypto.spec.ts index 0b16ea1ee5c..de93bc9771d 100644 --- a/spec/unit/rust-crypto/rust-crypto.spec.ts +++ b/spec/unit/rust-crypto/rust-crypto.spec.ts @@ -30,7 +30,6 @@ import fetchMock from "fetch-mock-jest"; import { RustCrypto } from "../../../src/rust-crypto/rust-crypto"; import { initRustCrypto } from "../../../src/rust-crypto"; import { - CryptoEvent, Device, DeviceVerification, encodeBase64, @@ -71,6 +70,7 @@ import { ClientEvent, ClientEventHandlerMap } from "../../../src/client"; import { Curve25519AuthData } from "../../../src/crypto-api/keybackup"; import encryptAESSecretStorageItem from "../../../src/utils/encryptAESSecretStorageItem.ts"; import { CryptoStore, SecretStorePrivateKeys } from "../../../src/crypto/store/base"; +import { CryptoEvent } from "../../../src/crypto-api/index.ts"; const TEST_USER = "@alice:example.com"; const TEST_DEVICE_ID = "TEST_DEVICE"; diff --git a/src/client.ts b/src/client.ts index 4059e68e63c..352c3c58ea9 100644 --- a/src/client.ts +++ b/src/client.ts @@ -76,8 +76,8 @@ import { } from "./http-api/index.ts"; import { Crypto, - CryptoEvent, - CryptoEventHandlerMap, + CryptoEvent as LegacyCryptoEvent, + CryptoEventHandlerMap as LegacyCryptoEventHandlerMap, fixBackupKey, ICheckOwnCrossSigningTrustOpts, ICryptoCallbacks, @@ -227,6 +227,8 @@ import { CryptoApi, decodeRecoveryKey, ImportRoomKeysOpts, + CryptoEvent, + CryptoEventHandlerMap, } from "./crypto-api/index.ts"; import { DeviceInfoMap } from "./crypto/DeviceList.ts"; import { @@ -939,23 +941,25 @@ type RoomStateEvents = | RoomStateEvent.Update | RoomStateEvent.Marker; -type CryptoEvents = - | CryptoEvent.KeySignatureUploadFailure - | CryptoEvent.KeyBackupStatus - | CryptoEvent.KeyBackupFailed - | CryptoEvent.KeyBackupSessionsRemaining - | CryptoEvent.KeyBackupDecryptionKeyCached - | CryptoEvent.RoomKeyRequest - | CryptoEvent.RoomKeyRequestCancellation - | CryptoEvent.VerificationRequest - | CryptoEvent.VerificationRequestReceived - | CryptoEvent.DeviceVerificationChanged - | CryptoEvent.UserTrustStatusChanged - | CryptoEvent.KeysChanged - | CryptoEvent.Warning - | CryptoEvent.DevicesUpdated - | CryptoEvent.WillUpdateDevices - | CryptoEvent.LegacyCryptoStoreMigrationProgress; +type LegacyCryptoEvents = + | LegacyCryptoEvent.KeySignatureUploadFailure + | LegacyCryptoEvent.KeyBackupStatus + | LegacyCryptoEvent.KeyBackupFailed + | LegacyCryptoEvent.KeyBackupSessionsRemaining + | LegacyCryptoEvent.KeyBackupDecryptionKeyCached + | LegacyCryptoEvent.RoomKeyRequest + | LegacyCryptoEvent.RoomKeyRequestCancellation + | LegacyCryptoEvent.VerificationRequest + | LegacyCryptoEvent.VerificationRequestReceived + | LegacyCryptoEvent.DeviceVerificationChanged + | LegacyCryptoEvent.UserTrustStatusChanged + | LegacyCryptoEvent.KeysChanged + | LegacyCryptoEvent.Warning + | LegacyCryptoEvent.DevicesUpdated + | LegacyCryptoEvent.WillUpdateDevices + | LegacyCryptoEvent.LegacyCryptoStoreMigrationProgress; + +type CryptoEvents = (typeof CryptoEvent)[keyof typeof CryptoEvent]; type MatrixEventEvents = MatrixEventEvent.Decrypted | MatrixEventEvent.Replaced | MatrixEventEvent.VisibilityChange; @@ -976,6 +980,7 @@ export type EmittedEvents = | ClientEvent | RoomEvents | RoomStateEvents + | LegacyCryptoEvents | CryptoEvents | MatrixEventEvents | RoomMemberEvents @@ -1187,6 +1192,7 @@ export type ClientEventHandlerMap = { [ClientEvent.TurnServersError]: (error: Error, fatal: boolean) => void; } & RoomEventHandlerMap & RoomStateEventHandlerMap & + LegacyCryptoEventHandlerMap & CryptoEventHandlerMap & MatrixEventHandlerMap & RoomMemberEventHandlerMap & @@ -2176,16 +2182,16 @@ export class MatrixClient extends TypedEventEmitter void; + [CryptoEvent.UserTrustStatusChanged]: (userId: string, userTrustLevel: UserVerificationStatus) => void; + [CryptoEvent.KeyBackupDecryptionKeyCached]: (version: string) => void; + [CryptoEvent.KeysChanged]: (data: {}) => void; + [CryptoEvent.WillUpdateDevices]: (users: string[], initialFetch: boolean) => void; + [CryptoEvent.DevicesUpdated]: (users: string[], initialFetch: boolean) => void; +} & RustBackupCryptoEventMap; diff --git a/src/crypto-api/index.ts b/src/crypto-api/index.ts index ec099dabff0..8a59b9c0507 100644 --- a/src/crypto-api/index.ts +++ b/src/crypto-api/index.ts @@ -1083,3 +1083,5 @@ export * from "./verification.ts"; export * from "./keybackup.ts"; export * from "./recovery-key.ts"; export * from "./key-passphrase.ts"; +export * from "./CryptoEvent.ts"; +export * from "./CryptoEventHandlerMap.ts"; diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 1b1cef084cc..34cf5c5db62 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -100,7 +100,8 @@ import { KeyBackupCheck, KeyBackupInfo, OwnDeviceKeys, - VerificationRequest as CryptoApiVerificationRequest, + CryptoEvent as CryptoApiCryptoEvent, + CryptoEventHandlerMap as CryptoApiCryptoEventHandlerMap, } from "../crypto-api/index.ts"; import { Device, DeviceMap } from "../models/device.ts"; import { deviceInfoToDevice } from "./device-converter.ts"; @@ -232,16 +233,16 @@ export type IEncryptedContent = IOlmEncryptedContent | IMegolmEncryptedContent; export enum CryptoEvent { /** @deprecated Event not fired by the rust crypto */ DeviceVerificationChanged = "deviceVerificationChanged", - UserTrustStatusChanged = "userTrustStatusChanged", + UserTrustStatusChanged = CryptoApiCryptoEvent.UserTrustStatusChanged, /** @deprecated Event not fired by the rust crypto */ UserCrossSigningUpdated = "userCrossSigningUpdated", /** @deprecated Event not fired by the rust crypto */ RoomKeyRequest = "crypto.roomKeyRequest", /** @deprecated Event not fired by the rust crypto */ RoomKeyRequestCancellation = "crypto.roomKeyRequestCancellation", - KeyBackupStatus = "crypto.keyBackupStatus", - KeyBackupFailed = "crypto.keyBackupFailed", - KeyBackupSessionsRemaining = "crypto.keyBackupSessionsRemaining", + KeyBackupStatus = CryptoApiCryptoEvent.KeyBackupStatus, + KeyBackupFailed = CryptoApiCryptoEvent.KeyBackupFailed, + KeyBackupSessionsRemaining = CryptoApiCryptoEvent.KeyBackupSessionsRemaining, /** * Fires when a new valid backup decryption key is in cache. @@ -252,7 +253,7 @@ export enum CryptoEvent { * * This event is only fired by the rust crypto backend. */ - KeyBackupDecryptionKeyCached = "crypto.keyBackupDecryptionKeyCached", + KeyBackupDecryptionKeyCached = CryptoApiCryptoEvent.KeyBackupDecryptionKeyCached, /** @deprecated Event not fired by the rust crypto */ KeySignatureUploadFailure = "crypto.keySignatureUploadFailure", @@ -264,14 +265,14 @@ export enum CryptoEvent { * * The payload is a {@link Crypto.VerificationRequest}. */ - VerificationRequestReceived = "crypto.verificationRequestReceived", + VerificationRequestReceived = CryptoApiCryptoEvent.VerificationRequestReceived, /** @deprecated Event not fired by the rust crypto */ Warning = "crypto.warning", /** @deprecated Use {@link DevicesUpdated} instead when using rust crypto */ - WillUpdateDevices = "crypto.willUpdateDevices", - DevicesUpdated = "crypto.devicesUpdated", - KeysChanged = "crossSigning.keysChanged", + WillUpdateDevices = CryptoApiCryptoEvent.WillUpdateDevices, + DevicesUpdated = CryptoApiCryptoEvent.DevicesUpdated, + KeysChanged = CryptoApiCryptoEvent.KeysChanged, /** * Fires when data is being migrated from legacy crypto to rust crypto. @@ -280,10 +281,10 @@ export enum CryptoEvent { * `total` is the total number of steps. When migration is complete, a final instance of the event is emitted, with * `progress === total === -1`. */ - LegacyCryptoStoreMigrationProgress = "crypto.legacyCryptoStoreMigrationProgress", + LegacyCryptoStoreMigrationProgress = CryptoApiCryptoEvent.LegacyCryptoStoreMigrationProgress, } -export type CryptoEventHandlerMap = { +export type CryptoEventHandlerMap = CryptoApiCryptoEventHandlerMap & { /** * Fires when a device is marked as verified/unverified/blocked/unblocked by * {@link MatrixClient#setDeviceVerified | MatrixClient.setDeviceVerified} or @@ -294,18 +295,6 @@ export type CryptoEventHandlerMap = { * @param deviceInfo - updated device information */ [CryptoEvent.DeviceVerificationChanged]: (userId: string, deviceId: string, deviceInfo: DeviceInfo) => void; - /** - * Fires when the trust status of a user changes - * If userId is the userId of the logged-in user, this indicated a change - * in the trust status of the cross-signing data on the account. - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * @experimental - * - * @param userId - the userId of the user in question - * @param trustLevel - The new trust level of the user - */ - [CryptoEvent.UserTrustStatusChanged]: (userId: string, trustLevel: UserTrustLevel) => void; /** * Fires when we receive a room key request * @@ -316,28 +305,6 @@ export type CryptoEventHandlerMap = { * Fires when we receive a room key request cancellation */ [CryptoEvent.RoomKeyRequestCancellation]: (request: IncomingRoomKeyRequestCancellation) => void; - /** - * Fires whenever the status of e2e key backup changes, as returned by getKeyBackupEnabled() - * @param enabled - true if key backup has been enabled, otherwise false - * @example - * ``` - * matrixClient.on("crypto.keyBackupStatus", function(enabled){ - * if (enabled) { - * [...] - * } - * }); - * ``` - */ - [CryptoEvent.KeyBackupStatus]: (enabled: boolean) => void; - [CryptoEvent.KeyBackupFailed]: (errcode: string) => void; - [CryptoEvent.KeyBackupSessionsRemaining]: (remaining: number) => void; - - /** - * Fires when the backup decryption key is received and cached. - * - * @param version - The version of the backup for which we have the key for. - */ - [CryptoEvent.KeyBackupDecryptionKeyCached]: (version: string) => void; [CryptoEvent.KeySignatureUploadFailure]: ( failures: IUploadKeySignaturesResponse["failures"], source: "checkOwnCrossSigningTrust" | "afterCrossSigningLocalKeyChange" | "setDeviceVerification", @@ -349,12 +316,6 @@ export type CryptoEventHandlerMap = { * Deprecated: use `CryptoEvent.VerificationRequestReceived`. */ [CryptoEvent.VerificationRequest]: (request: VerificationRequest) => void; - - /** - * Fires when a key verification request is received. - */ - [CryptoEvent.VerificationRequestReceived]: (request: CryptoApiVerificationRequest) => void; - /** * Fires when the app may wish to warn the user about something related * the end-to-end crypto. @@ -362,33 +323,6 @@ export type CryptoEventHandlerMap = { * @param type - One of the strings listed above */ [CryptoEvent.Warning]: (type: string) => void; - /** - * Fires when the user's cross-signing keys have changed or cross-signing - * has been enabled/disabled. The client can use getStoredCrossSigningForUser - * with the user ID of the logged in user to check if cross-signing is - * enabled on the account. If enabled, it can test whether the current key - * is trusted using with checkUserTrust with the user ID of the logged - * in user. The checkOwnCrossSigningTrust function may be used to reconcile - * the trust in the account key. - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * @experimental - */ - [CryptoEvent.KeysChanged]: (data: {}) => void; - /** - * Fires whenever the stored devices for a user will be updated - * @param users - A list of user IDs that will be updated - * @param initialFetch - If true, the store is empty (apart - * from our own device) and is being seeded. - */ - [CryptoEvent.WillUpdateDevices]: (users: string[], initialFetch: boolean) => void; - /** - * Fires whenever the stored devices for a user have changed - * @param users - A list of user IDs that were updated - * @param initialFetch - If true, the store was empty (apart - * from our own device) and has been seeded. - */ - [CryptoEvent.DevicesUpdated]: (users: string[], initialFetch: boolean) => void; [CryptoEvent.UserCrossSigningUpdated]: (userId: string) => void; [CryptoEvent.LegacyCryptoStoreMigrationProgress]: (progress: number, total: number) => void; diff --git a/src/rust-crypto/PerSessionKeyBackupDownloader.ts b/src/rust-crypto/PerSessionKeyBackupDownloader.ts index b33c17e1546..4e066d31e74 100644 --- a/src/rust-crypto/PerSessionKeyBackupDownloader.ts +++ b/src/rust-crypto/PerSessionKeyBackupDownloader.ts @@ -18,10 +18,10 @@ import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; import { OlmMachine } from "@matrix-org/matrix-sdk-crypto-wasm"; import { Curve25519AuthData, KeyBackupInfo, KeyBackupSession } from "../crypto-api/keybackup.ts"; +import { CryptoEvent } from "../crypto-api/index.ts"; import { Logger } from "../logger.ts"; import { ClientPrefix, IHttpOpts, MatrixError, MatrixHttpApi, Method } from "../http-api/index.ts"; import { RustBackupManager } from "./backup.ts"; -import { CryptoEvent } from "../matrix.ts"; import { encodeUri, sleep } from "../utils.ts"; import { BackupDecryptor } from "../common-crypto/CryptoBackend.ts"; diff --git a/src/rust-crypto/backup.ts b/src/rust-crypto/backup.ts index b83a8e7a712..4c868f12ad0 100644 --- a/src/rust-crypto/backup.ts +++ b/src/rust-crypto/backup.ts @@ -27,13 +27,13 @@ import { } from "../crypto-api/keybackup.ts"; import { logger } from "../logger.ts"; import { ClientPrefix, IHttpOpts, MatrixError, MatrixHttpApi, Method } from "../http-api/index.ts"; -import { CryptoEvent, IMegolmSessionData } from "../crypto/index.ts"; +import { IMegolmSessionData } from "../crypto/index.ts"; import { TypedEventEmitter } from "../models/typed-event-emitter.ts"; import { encodeUri, logDuration } from "../utils.ts"; import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor.ts"; import { sleep } from "../utils.ts"; import { BackupDecryptor } from "../common-crypto/CryptoBackend.ts"; -import { ImportRoomKeyProgressData, ImportRoomKeysOpts } from "../crypto-api/index.ts"; +import { ImportRoomKeyProgressData, ImportRoomKeysOpts, CryptoEvent } from "../crypto-api/index.ts"; import { IKeyBackupInfo } from "../crypto/keybackup.ts"; import { IKeyBackup } from "../crypto/backup.ts"; import { AESEncryptedSecretStoragePayload } from "../@types/AESEncryptedSecretStoragePayload.ts"; diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts index 78f921e3b55..3b12c009db7 100644 --- a/src/rust-crypto/rust-crypto.ts +++ b/src/rust-crypto/rust-crypto.ts @@ -63,6 +63,8 @@ import { DeviceIsolationMode, AllDevicesIsolationMode, DeviceIsolationModeKind, + CryptoEvent, + CryptoEventHandlerMap, } from "../crypto-api/index.ts"; import { deviceKeysToDeviceMap, rustDeviceToJsDevice } from "./device-converter.ts"; import { IDownloadKeyResult, IQueryKeysRequest } from "../client.ts"; @@ -72,9 +74,8 @@ import { CrossSigningIdentity } from "./CrossSigningIdentity.ts"; import { secretStorageCanAccessSecrets, secretStorageContainsCrossSigningKeys } from "./secret-storage.ts"; import { isVerificationEvent, RustVerificationRequest, verificationMethodIdentifierToMethod } from "./verification.ts"; import { EventType, MsgType } from "../@types/event.ts"; -import { CryptoEvent } from "../crypto/index.ts"; import { TypedEventEmitter } from "../models/typed-event-emitter.ts"; -import { RustBackupCryptoEventMap, RustBackupCryptoEvents, RustBackupManager } from "./backup.ts"; +import { RustBackupManager } from "./backup.ts"; import { TypedReEmitter } from "../ReEmitter.ts"; import { randomString } from "../randomstring.ts"; import { ClientStoppedError } from "../errors.ts"; @@ -102,7 +103,7 @@ interface ISignableObject { * * @internal */ -export class RustCrypto extends TypedEventEmitter implements CryptoBackend { +export class RustCrypto extends TypedEventEmitter implements CryptoBackend { /** * The number of iterations to use when deriving a recovery key from a passphrase. */ @@ -125,7 +126,7 @@ export class RustCrypto extends TypedEventEmitter(this); + private readonly reemitter = new TypedReEmitter(this); public constructor( private readonly logger: Logger, @@ -2077,51 +2078,5 @@ function rustEncryptionInfoToJsEncryptionInfo( return { shieldColour, shieldReason }; } -type RustCryptoEvents = - | CryptoEvent.VerificationRequestReceived - | CryptoEvent.UserTrustStatusChanged - | CryptoEvent.KeysChanged - | CryptoEvent.WillUpdateDevices - | CryptoEvent.DevicesUpdated - | RustBackupCryptoEvents; - -type RustCryptoEventMap = { - /** - * Fires when a key verification request is received. - */ - [CryptoEvent.VerificationRequestReceived]: (request: VerificationRequest) => void; - - /** - * Fires when the trust status of a user changes. - */ - [CryptoEvent.UserTrustStatusChanged]: (userId: string, userTrustLevel: UserVerificationStatus) => void; - - [CryptoEvent.KeyBackupDecryptionKeyCached]: (version: string) => void; - /** - * Fires when the user's cross-signing keys have changed or cross-signing - * has been enabled/disabled. The client can use getStoredCrossSigningForUser - * with the user ID of the logged in user to check if cross-signing is - * enabled on the account. If enabled, it can test whether the current key - * is trusted using with checkUserTrust with the user ID of the logged - * in user. The checkOwnCrossSigningTrust function may be used to reconcile - * the trust in the account key. - * - * The cross-signing API is currently UNSTABLE and may change without notice. - * @experimental - */ - [CryptoEvent.KeysChanged]: (data: {}) => void; - /** - * Fires whenever the stored devices for a user will be updated - * @param users - A list of user IDs that will be updated - * @param initialFetch - If true, the store is empty (apart - * from our own device) and is being seeded. - */ - [CryptoEvent.WillUpdateDevices]: (users: string[], initialFetch: boolean) => void; - /** - * Fires whenever the stored devices for a user have changed - * @param users - A list of user IDs that were updated - * @param initialFetch - If true, the store was empty (apart - * from our own device) and has been seeded. - */ - [CryptoEvent.DevicesUpdated]: (users: string[], initialFetch: boolean) => void; -} & RustBackupCryptoEventMap; +type CryptoEvents = (typeof CryptoEvent)[keyof typeof CryptoEvent]; +type RustCryptoEvents = Exclude;