diff --git a/CHANGELOG.md b/CHANGELOG.md index d0a5e1cd743..9b4264fc1bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,22 @@ +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 + +* 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/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 05272202499..c273cbfccfc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-js-sdk", - "version": "34.6.0", + "version": "34.8.0", "description": "Matrix Client-Server SDK for Javascript", "engines": { "node": ">=20.0.0" @@ -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.1", "eslint-config-google": "^0.14.0", @@ -104,7 +105,7 @@ "eslint-plugin-tsdoc": "^0.3.0", "eslint-plugin-unicorn": "^55.0.0", "fake-indexeddb": "^5.0.2", - "fetch-mock": "11.1.3", + "fetch-mock": "11.1.5", "fetch-mock-jest": "^1.5.1", "husky": "^9.0.0", "jest": "^29.0.0", diff --git a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts index 0e1f81498a9..d19038ff250 100644 --- a/spec/unit/matrixrtc/MatrixRTCSession.spec.ts +++ b/spec/unit/matrixrtc/MatrixRTCSession.spec.ts @@ -1010,6 +1010,48 @@ describe("MatrixRTCSession", () => { } }); + it("Wraps key index around to 0 when it reaches the maximum", async () => { + // this should give us keys with index [0...255, 0, 1] + const membersToTest = 258; + const members: CallMembershipData[] = []; + for (let i = 0; i < membersToTest; i++) { + members.push(Object.assign({}, membershipTemplate, { device_id: `DEVICE${i}` })); + } + jest.useFakeTimers(); + try { + // start with a single member + const mockRoom = makeMockRoom(members.slice(0, 1)); + + for (let i = 0; i < membersToTest; i++) { + const keysSentPromise = new Promise((resolve) => { + sendEventMock.mockImplementation((_roomId, _evType, payload) => resolve(payload)); + }); + + if (i === 0) { + // if first time around then set up the session + sess = MatrixRTCSession.roomSessionForRoom(client, mockRoom); + sess.joinRoomSession([mockFocus], mockFocus, { manageMediaKeys: true }); + } else { + // otherwise update the state + mockRoom.getLiveTimeline().getState = jest + .fn() + .mockReturnValue(makeMockRoomState(members.slice(0, i + 1), mockRoom.roomId)); + } + + sess!.onMembershipUpdate(); + + // advance time to avoid key throttling + jest.advanceTimersByTime(10000); + + const keysPayload = await keysSentPromise; + expect(keysPayload.keys).toHaveLength(1); + expect(keysPayload.keys[0].index).toEqual(i % 256); + } + } finally { + jest.useRealTimers(); + } + }); + it("Doesn't re-send key immediately", async () => { const realSetTimeout = setTimeout; jest.useFakeTimers(); 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/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/spec/unit/secret-storage.spec.ts b/spec/unit/secret-storage.spec.ts index b71da45e2f8..b2346d88e6c 100644 --- a/spec/unit/secret-storage.spec.ts +++ b/spec/unit/secret-storage.spec.ts @@ -18,6 +18,7 @@ import { Mocked } from "jest-mock"; import { AccountDataClient, + calculateKeyCheck, PassphraseInfo, SecretStorageCallbacks, SecretStorageKeyDescriptionAesV1, @@ -26,7 +27,6 @@ import { trimTrailingEquals, } from "../../src/secret-storage"; import { randomString } from "../../src/randomstring"; -import { calculateKeyCheck } from "../../src/calculateKeyCheck.ts"; describe("ServerSideSecretStorageImpl", function () { describe(".addKey", function () { diff --git a/src/@types/requests.ts b/src/@types/requests.ts index 0baedff3ff4..cbc82aa7b5b 100644 --- a/src/@types/requests.ts +++ b/src/@types/requests.ts @@ -111,18 +111,16 @@ type DelayedPartialTimelineEvent = { type DelayedPartialStateEvent = DelayedPartialTimelineEvent & { state_key: string; - transaction_id: string; }; type DelayedPartialEvent = DelayedPartialTimelineEvent | DelayedPartialStateEvent; export type DelayedEventInfo = { - delayed_events: DelayedPartialEvent & + delayed_events: (DelayedPartialEvent & SendDelayedEventResponse & - SendDelayedEventRequestOpts & - { + SendDelayedEventRequestOpts & { running_since: number; - }[]; + })[]; next_batch?: string; }; diff --git a/src/calculateKeyCheck.ts b/src/calculateKeyCheck.ts deleted file mode 100644 index 7e979a71263..00000000000 --- a/src/calculateKeyCheck.ts +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2024 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. - */ - -// string of zeroes, for calculating the key check -import encryptAESSecretStorageItem from "./utils/encryptAESSecretStorageItem.ts"; -import { AESEncryptedSecretStoragePayload } from "./@types/AESEncryptedSecretStoragePayload.ts"; - -const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; - -/** - * Calculate the MAC for checking the key. - * See https://spec.matrix.org/v1.11/client-server-api/#msecret_storagev1aes-hmac-sha2, steps 3 and 4. - * - * @param key - the key to use - * @param iv - The initialization vector as a base64-encoded string. - * If omitted, a random initialization vector will be created. - * @returns An object that contains, `mac` and `iv` properties. - */ -export function calculateKeyCheck(key: Uint8Array, iv?: string): Promise { - return encryptAESSecretStorageItem(ZERO_STR, key, "", iv); -} diff --git a/src/client.ts b/src/client.ts index 3a9860b3a8b..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 { - 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. diff --git a/src/crypto-api/CryptoEvent.ts b/src/crypto-api/CryptoEvent.ts new file mode 100644 index 00000000000..9ddf26476b5 --- /dev/null +++ b/src/crypto-api/CryptoEvent.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2024 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. + */ + +/** + * Events emitted by the {@link CryptoApi} + */ +export enum CryptoEvent { + /** + * Fires when the trust status of a user changes. + * The payload is a pair (userId, userTrustLevel). The trust level is one of the values from UserVerificationStatus. + */ + UserTrustStatusChanged = "userTrustStatusChanged", + + /** + * Fires when the key backup status changes. + * The payload is a boolean indicating whether the key backup is enabled. + */ + KeyBackupStatus = "crypto.keyBackupStatus", + + /** + * Fires when we failed to back up the keys + * The payload is the error code of the error that occurred. + */ + KeyBackupFailed = "crypto.keyBackupFailed", + + /** + * Fires when the number of sessions that can be backed up changes. + * The payload is the remaining number of sessions that can be backed up. + */ + KeyBackupSessionsRemaining = "crypto.keyBackupSessionsRemaining", + + /** + * Fires when a new valid backup decryption key is in cache. + * This will happen when a secret is received from another session, from secret storage, + * or when a new backup is created from this session. + * + * The payload is the version of the backup for which we have the key for. + * + * This event is only fired by the rust crypto backend. + */ + KeyBackupDecryptionKeyCached = "crypto.keyBackupDecryptionKeyCached", + + /** + * Fires when a key verification request is received. + * The payload is a VerificationRequest object representing the request. + */ + VerificationRequestReceived = "crypto.verificationRequestReceived", + + /** @deprecated Use {@link DevicesUpdated} instead when using rust crypto */ + WillUpdateDevices = "crypto.willUpdateDevices", + + /** + * Fires whenever the stored devices for a user have been updated + * The payload is a pair (userIds, initialFetch). + */ + DevicesUpdated = "crypto.devicesUpdated", + + /** + * 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 + */ + KeysChanged = "crossSigning.keysChanged", + + /** + * Fires when data is being migrated from legacy crypto to rust crypto. + * + * The payload is a pair `(progress, total)`, where `progress` is the number of steps completed so far, and + * `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", +} diff --git a/src/crypto-api/CryptoEventHandlerMap.ts b/src/crypto-api/CryptoEventHandlerMap.ts new file mode 100644 index 00000000000..b7acb105aad --- /dev/null +++ b/src/crypto-api/CryptoEventHandlerMap.ts @@ -0,0 +1,32 @@ +/* + * Copyright 2024 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 { CryptoEvent } from "./CryptoEvent.ts"; +import { VerificationRequest } from "./verification.ts"; +import { UserVerificationStatus } from "./index.ts"; +import { RustBackupCryptoEventMap } from "../rust-crypto/backup.ts"; + +/** + * A map of the {@link CryptoEvent} fired by the {@link CryptoApi} and their payloads. + */ +export type CryptoEventHandlerMap = { + [CryptoEvent.VerificationRequestReceived]: (request: VerificationRequest) => 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 0ac684e8c63..8a59b9c0507 100644 --- a/src/crypto-api/index.ts +++ b/src/crypto-api/index.ts @@ -25,6 +25,15 @@ import { BackupTrustInfo, KeyBackupCheck, KeyBackupInfo } from "./keybackup.ts"; import { ISignatures } from "../@types/signed.ts"; import { MatrixEvent } from "../models/event.ts"; +/** + * `matrix-js-sdk/lib/crypto-api`: End-to-end encryption support. + * + * The most important type is {@link CryptoApi}, an instance of which can be retrieved via + * {@link MatrixClient.getCrypto}. + * + * @packageDocumentation + */ + /** * Public interface to the cryptography parts of the js-sdk * @@ -181,7 +190,7 @@ export interface CryptoApi { /** * Return whether we trust other user's signatures of their devices. * - * @see {@link Crypto.CryptoApi#setTrustCrossSignedDevices} + * @see {@link CryptoApi.setTrustCrossSignedDevices} * * @returns `true` if we trust cross-signed devices, otherwise `false`. */ @@ -228,7 +237,7 @@ export interface CryptoApi { * * @throws an error if the device is unknown, or has not published any encryption keys. * - * @remarks Fires {@link CryptoEvent#DeviceVerificationChanged} + * @remarks Fires {@link matrix.CryptoEvent.DeviceVerificationChanged} */ setDeviceVerified(userId: string, deviceId: string, verified?: boolean): Promise; @@ -259,7 +268,7 @@ export interface CryptoApi { * * @returns True if cross-signing is ready to be used on this device * - * @throws May throw {@link ClientStoppedError} if the `MatrixClient` is stopped before or during the call. + * @throws May throw {@link matrix.ClientStoppedError} if the `MatrixClient` is stopped before or during the call. */ isCrossSigningReady(): Promise; @@ -327,7 +336,7 @@ export interface CryptoApi { * @returns The current status of cross-signing keys: whether we have public and private keys cached locally, and * whether the private keys are in secret storage. * - * @throws May throw {@link ClientStoppedError} if the `MatrixClient` is stopped before or during the call. + * @throws May throw {@link matrix.ClientStoppedError} if the `MatrixClient` is stopped before or during the call. */ getCrossSigningStatus(): Promise; @@ -407,8 +416,8 @@ export interface CryptoApi { * * If an all-devices verification is already in flight, returns it. Otherwise, initiates a new one. * - * To control the methods offered, set {@link ICreateClientOpts.verificationMethods} when creating the - * MatrixClient. + * To control the methods offered, set {@link matrix.ICreateClientOpts.verificationMethods} when creating the + * `MatrixClient`. * * @returns a VerificationRequest when the request has been sent to the other party. */ @@ -422,8 +431,8 @@ export interface CryptoApi { * * If a verification for this user/device is already in flight, returns it. Otherwise, initiates a new one. * - * To control the methods offered, set {@link ICreateClientOpts.verificationMethods} when creating the - * MatrixClient. + * To control the methods offered, set {@link matrix.ICreateClientOpts.verificationMethods} when creating the + * `MatrixClient`. * * @param userId - ID of the owner of the device to verify * @param deviceId - ID of the device to verify @@ -480,7 +489,7 @@ export interface CryptoApi { /** * Determine if a key backup can be trusted. * - * @param info - key backup info dict from {@link MatrixClient#getKeyBackupVersion}. + * @param info - key backup info dict from {@link matrix.MatrixClient.getKeyBackupVersion}. */ isKeyBackupTrusted(info: KeyBackupInfo): Promise; @@ -500,7 +509,7 @@ export interface CryptoApi { * * If there are existing backups they will be replaced. * - * The decryption key will be saved in Secret Storage (the {@link SecretStorageCallbacks.getSecretStorageKey} Crypto + * The decryption key will be saved in Secret Storage (the {@link matrix.SecretStorage.SecretStorageCallbacks.getSecretStorageKey} Crypto * callback will be called) * and the backup engine will be started. */ @@ -841,9 +850,9 @@ export class DeviceVerificationStatus { * Check if we should consider this device "verified". * * A device is "verified" if either: - * * it has been manually marked as such via {@link MatrixClient#setDeviceVerified}. + * * it has been manually marked as such via {@link matrix.MatrixClient.setDeviceVerified}. * * it has been cross-signed with a verified signing key, **and** the client has been configured to trust - * cross-signed devices via {@link Crypto.CryptoApi#setTrustCrossSignedDevices}. + * cross-signed devices via {@link CryptoApi.setTrustCrossSignedDevices}. * * @returns true if this device is verified via any means. */ @@ -1074,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-api/keybackup.ts b/src/crypto-api/keybackup.ts index 8a82a5f4c13..efae30d0ec4 100644 --- a/src/crypto-api/keybackup.ts +++ b/src/crypto-api/keybackup.ts @@ -35,7 +35,7 @@ export interface Aes256AuthData { * Information about a server-side key backup. * * Returned by [`GET /_matrix/client/v3/room_keys/version`](https://spec.matrix.org/v1.7/client-server-api/#get_matrixclientv3room_keysversion) - * and hence {@link MatrixClient#getKeyBackupVersion}. + * and hence {@link matrix.MatrixClient.getKeyBackupVersion}. */ export interface KeyBackupInfo { algorithm: string; @@ -63,7 +63,7 @@ export interface BackupTrustInfo { } /** - * The result of {@link Crypto.CryptoApi.checkKeyBackupAndEnable}. + * The result of {@link CryptoApi.checkKeyBackupAndEnable}. */ export interface KeyBackupCheck { backupInfo: KeyBackupInfo; diff --git a/src/crypto-api/verification.ts b/src/crypto-api/verification.ts index daa7bdbceed..468a5d7a9f2 100644 --- a/src/crypto-api/verification.ts +++ b/src/crypto-api/verification.ts @@ -119,7 +119,7 @@ export interface VerificationRequest * * If a verifier has already been created for this request, returns that verifier. * - * This does *not* send the `m.key.verification.start` event - to do so, call {@link Crypto.Verifier#verify} on the + * This does *not* send the `m.key.verification.start` event - to do so, call {@link Verifier.verify} on the * returned verifier. * * If no previous events have been sent, pass in `targetDevice` to set who to direct this request to. @@ -281,7 +281,7 @@ export interface Verifier extends TypedEventEmitter 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 * @@ -309,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", @@ -342,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. @@ -355,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/matrix.ts b/src/matrix.ts index 7727470404f..29c619fa821 100644 --- a/src/matrix.ts +++ b/src/matrix.ts @@ -93,7 +93,11 @@ export { GroupCallType, GroupCallStatsReportEvent, } from "./webrtc/groupCall.ts"; -export { CryptoEvent } from "./crypto/index.ts"; + +export { + /** @deprecated Use {@link Crypto.CryptoEvent} instead */ + CryptoEvent, +} from "./crypto/index.ts"; export { SyncState, SetPresence } from "./sync.ts"; export type { ISyncStateData as SyncStateData } from "./sync.ts"; export { SlidingSyncEvent } from "./sliding-sync.ts"; @@ -108,12 +112,7 @@ export type { ISSOFlow as SSOFlow, LoginFlow } from "./@types/auth.ts"; export type { IHierarchyRelation as HierarchyRelation, IHierarchyRoom as HierarchyRoom } from "./@types/spaces.ts"; export { LocationAssetType } from "./@types/location.ts"; -/** - * Types supporting cryptography. - * - * The most important is {@link Crypto.CryptoApi}, an instance of which can be retrieved via - * {@link MatrixClient.getCrypto}. - */ +/** @deprecated Backwards-compatibility re-export. Import from `crypto-api` directly. */ export * as Crypto from "./crypto-api/index.ts"; let cryptoStoreFactory = (): CryptoStore => new MemoryCryptoStore(); diff --git a/src/matrixrtc/MatrixRTCSession.ts b/src/matrixrtc/MatrixRTCSession.ts index 1e92793807d..a76df9afd44 100644 --- a/src/matrixrtc/MatrixRTCSession.ts +++ b/src/matrixrtc/MatrixRTCSession.ts @@ -448,13 +448,12 @@ export class MatrixRTCSession extends TypedEventEmitter { + 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 { diff --git a/src/models/typed-event-emitter.ts b/src/models/typed-event-emitter.ts index c3bec68d74b..3c90710165e 100644 --- a/src/models/typed-event-emitter.ts +++ b/src/models/typed-event-emitter.ts @@ -65,7 +65,7 @@ export class TypedEventEmitter< SuperclassArguments extends ListenerMap = Arguments, > extends EventEmitter { /** - * Alias for {@link TypedEventEmitter#on}. + * Alias for {@link on}. */ public addListener( event: T, @@ -124,7 +124,7 @@ export class TypedEventEmitter< } /** - * Alias for {@link TypedEventEmitter#removeListener} + * Alias for {@link removeListener} */ public off(event: T, listener: Listener): this { return super.off(event, listener); @@ -139,7 +139,7 @@ export class TypedEventEmitter< * being added, and called, multiple times. * * By default, event listeners are invoked in the order they are added. The - * {@link TypedEventEmitter#prependListener} method can be used as an alternative to add the + * {@link prependListener} method can be used as an alternative to add the * event listener to the beginning of the listeners array. * * @param event - The name of the event. @@ -158,7 +158,7 @@ export class TypedEventEmitter< * Returns a reference to the `EventEmitter`, so that calls can be chained. * * By default, event listeners are invoked in the order they are added. - * The {@link TypedEventEmitter#prependOnceListener} method can be used as an alternative to add the + * The {@link prependOnceListener} method can be used as an alternative to add the * event listener to the beginning of the listeners array. * * @param event - The name of the event. 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; diff --git a/src/secret-storage.ts b/src/secret-storage.ts index f192ddfd7f5..2aa8a028aa7 100644 --- a/src/secret-storage.ts +++ b/src/secret-storage.ts @@ -28,7 +28,6 @@ import { logger } from "./logger.ts"; import encryptAESSecretStorageItem from "./utils/encryptAESSecretStorageItem.ts"; import decryptAESSecretStorageItem from "./utils/decryptAESSecretStorageItem.ts"; import { AESEncryptedSecretStoragePayload } from "./@types/AESEncryptedSecretStoragePayload.ts"; -import { calculateKeyCheck } from "./crypto/aes.ts"; export const SECRET_STORAGE_ALGORITHM_V1_AES = "m.secret_storage.v1.aes-hmac-sha2"; @@ -165,7 +164,7 @@ export interface SecretStorageCallbacks { * Descriptions of the secret storage keys are also stored in server-side storage, per the * [matrix specification](https://spec.matrix.org/v1.6/client-server-api/#key-storage), so * before a key can be used in this way, it must have been stored on the server. This is - * done via {@link SecretStorage.ServerSideSecretStorage#addKey}. + * done via {@link ServerSideSecretStorage#addKey}. * * Obviously the keys themselves are not stored server-side, so the js-sdk calls this callback * in order to retrieve a secret storage key from the application. @@ -676,3 +675,19 @@ export function trimTrailingEquals(input: string): string { return input; } } + +// string of zeroes, for calculating the key check +const ZERO_STR = "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"; + +/** + * Calculate the MAC for checking the key. + * See https://spec.matrix.org/v1.11/client-server-api/#msecret_storagev1aes-hmac-sha2, steps 3 and 4. + * + * @param key - the key to use + * @param iv - The initialization vector as a base64-encoded string. + * If omitted, a random initialization vector will be created. + * @returns An object that contains, `mac` and `iv` properties. + */ +export function calculateKeyCheck(key: Uint8Array, iv?: string): Promise { + return encryptAESSecretStorageItem(ZERO_STR, key, "", iv); +} diff --git a/src/store/indexeddb.ts b/src/store/indexeddb.ts index 67d219e2bb3..443447b650b 100644 --- a/src/store/indexeddb.ts +++ b/src/store/indexeddb.ts @@ -24,7 +24,7 @@ import { logger } from "../logger.ts"; import { ISavedSync } from "./index.ts"; import { IIndexedDBBackend } from "./indexeddb-backend.ts"; import { ISyncResponse } from "../sync-accumulator.ts"; -import { TypedEventEmitter } from "../models/typed-event-emitter.ts"; +import { EventEmitterEvents, TypedEventEmitter } from "../models/typed-event-emitter.ts"; import { IStateEventWithRoomId } from "../@types/search.ts"; import { IndexedToDeviceBatch, ToDeviceBatchWithTxnId } from "../models/ToDeviceMessage.ts"; import { IStoredClientOpts } from "../client.ts"; @@ -118,7 +118,10 @@ export class IndexedDBStore extends MemoryStore { } } - public on = this.emitter.on.bind(this.emitter); + /** Re-exports `TypedEventEmitter.on` */ + public on(event: EventEmitterEvents | "degraded" | "closed", handler: (...args: any[]) => void): void { + this.emitter.on(event, handler); + } /** * @returns Resolved when loaded from indexed db. diff --git a/typedoc.json b/typedoc.json index 75b85b15bf8..9f4abe4103f 100644 --- a/typedoc.json +++ b/typedoc.json @@ -2,7 +2,7 @@ "$schema": "https://typedoc.org/schema.json", "plugin": ["typedoc-plugin-mdn-links", "typedoc-plugin-missing-exports", "typedoc-plugin-coverage"], "coverageLabel": "TypeDoc", - "entryPoints": ["src/matrix.ts", "src/types.ts", "src/testing.ts", "src/utils/*.ts"], + "entryPoints": ["src/matrix.ts", "src/crypto-api", "src/types.ts", "src/testing.ts", "src/utils/*.ts"], "excludeExternals": true, "out": "_docs" } diff --git a/yarn.lock b/yarn.lock index f77892d2f31..99a229ce9cd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1796,9 +1796,9 @@ undici-types "~5.26.4" "@types/node@18": - version "18.19.50" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.50.tgz#8652b34ee7c0e7e2004b3f08192281808d41bf5a" - integrity sha512-xonK+NRrMBRtkL1hVCc3G+uXtjh1Al4opBLjqVmipe5ZAaBYWW6cNAiBVZ1BvmkBhep698rP3UM3aRAdSALuhg== + version "18.19.54" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.54.tgz#f1048dc083f81b242640f04f18fb3e4ccf13fcdb" + integrity sha512-+BRgt0G5gYjTvdLac9sIeE0iZcJxi4Jc4PV5EUzqi+88jmQLr+fRZdv2tCTV7IHKSGxM6SaLoOXQWWUiLUItMw== dependencies: undici-types "~5.26.4" @@ -2278,6 +2278,11 @@ babel-plugin-polyfill-regenerator@^0.6.1: dependencies: "@babel/helper-define-polyfill-provider" "^0.6.2" +babel-plugin-search-and-replace@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/babel-plugin-search-and-replace/-/babel-plugin-search-and-replace-1.1.1.tgz#2e5b4488e41d9eba1c220651b1a9b350fdf10915" + integrity sha512-fjP2VTF3mxxOUnc96mdK22llH92A6gu7A5AFapJmgnqsQi3bqLduIRP0FpA2r5vRZOYPpnX4rE5izQlpsMBjSA== + babel-preset-current-node-syntax@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz#b4399239b89b2a011f9ddbe3e4f401fc40cff73b" @@ -3128,9 +3133,9 @@ eslint-plugin-jest@^28.0.0: "@typescript-eslint/utils" "^6.0.0 || ^7.0.0 || ^8.0.0" eslint-plugin-jsdoc@^50.0.0: - version "50.2.4" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.2.4.tgz#9abef68ea5869e87d8a4444bfef9e5a7787162e2" - integrity sha512-020jA+dXaXdb+TML3ZJBvpPmzwbNROjnYuTYi/g6A5QEmEjhptz4oPJDKkOGMIByNxsPpdTLzSU1HYVqebOX1w== + version "50.3.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.3.1.tgz#4b56340a3ef55df9957dede430c6338c717dc0dc" + integrity sha512-SY9oUuTMr6aWoJggUS40LtMjsRzJPB5ZT7F432xZIHK3EfHF+8i48GbUBpwanrtlL9l1gILNTHK9o8gEhYLcKA== dependencies: "@es-joy/jsdoccomment" "~0.48.0" are-docs-informative "^0.0.2" @@ -3234,10 +3239,10 @@ eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4 resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== -eslint-visitor-keys@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz#e3adc021aa038a2a8e0b2f8b0ce8f66b9483b1fb" - integrity sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw== +eslint-visitor-keys@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz#1f785cc5e81eb7534523d85922248232077d2f8c" + integrity sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg== eslint@8.57.1: version "8.57.1" @@ -3284,13 +3289,13 @@ eslint@8.57.1: text-table "^0.2.0" espree@^10.1.0: - version "10.1.0" - resolved "https://registry.yarnpkg.com/espree/-/espree-10.1.0.tgz#8788dae611574c0f070691f522e4116c5a11fc56" - integrity sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA== + version "10.2.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.2.0.tgz#f4bcead9e05b0615c968e85f83816bc386a45df6" + integrity sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g== dependencies: acorn "^8.12.0" acorn-jsx "^5.3.2" - eslint-visitor-keys "^4.0.0" + eslint-visitor-keys "^4.1.0" espree@^9.6.0, espree@^9.6.1: version "9.6.1" @@ -3454,10 +3459,10 @@ fetch-mock-jest@^1.5.1: dependencies: fetch-mock "^9.11.0" -fetch-mock@11.1.3: - version "11.1.3" - resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-11.1.3.tgz#3dc8dcc81340fea1cf1db60a2c195c63e270b501" - integrity sha512-ATh0dWgnVrUHiiXuvQm1Ry+ThWfSv1QQgqJTCtybrNxyUrFiSOaDKsNG29eyysp1SHeNP6Q+dH50+8VifN51Ig== +fetch-mock@11.1.5: + version "11.1.5" + resolved "https://registry.yarnpkg.com/fetch-mock/-/fetch-mock-11.1.5.tgz#77f78942f3733cfba47fc232b8528d1138a6761f" + integrity sha512-KHmZDnZ1ry0pCTrX4YG5DtThHi0MH+GNI9caESnzX/nMJBrvppUHMvLx47M0WY9oAtKOMiPfZDRpxhlHg89BOA== dependencies: "@types/glob-to-regexp" "^0.4.4" dequal "^2.0.3" @@ -4754,9 +4759,9 @@ kleur@^3.0.3: integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w== knip@^5.0.0: - version "5.30.2" - resolved "https://registry.yarnpkg.com/knip/-/knip-5.30.2.tgz#3cee8df2e1eb0c858f81e64568b72c0a23b94479" - integrity sha512-UuUwuTN+6Aa6mjxufWwidayGX/tPJsxZSlwUo8q4R+Gf/0aNYuhHsUH/GccuKtRPfFnf3r+ZU/7BV0dNfC7OEQ== + version "5.30.6" + resolved "https://registry.yarnpkg.com/knip/-/knip-5.30.6.tgz#798dd6a7fe338025166b8b138bb183d6b8714b75" + integrity sha512-YkcnRVl0N99xZ7eaXE7KlH/4cPTCn6BGuk9KxINEdCMFN3yita2vGBizApy97ZOHgghy8tb589gQ3xvLMFIO4w== dependencies: "@nodelib/fs.walk" "1.2.8" "@snyk/github-codeowners" "1.1.0" @@ -4769,7 +4774,7 @@ knip@^5.0.0: picocolors "^1.0.0" picomatch "^4.0.1" pretty-ms "^9.0.0" - smol-toml "^1.1.4" + smol-toml "^1.3.0" strip-json-comments "5.0.1" summary "2.1.0" zod "^3.22.4" @@ -5321,9 +5326,9 @@ parent-module@^1.0.0: callsites "^3.0.0" parse-imports@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/parse-imports/-/parse-imports-2.1.1.tgz#ce52141df24990065d72a446a364bffd595577f4" - integrity sha512-TDT4HqzUiTMO1wJRwg/t/hYk8Wdp3iF/ToMIlAoVQfL1Xs/sTxq1dKWSMjMbQmIarfWKymOyly40+zmPHXMqCA== + version "2.2.1" + resolved "https://registry.yarnpkg.com/parse-imports/-/parse-imports-2.2.1.tgz#0a6e8b5316beb5c9905f50eb2bbb8c64a4805642" + integrity sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ== dependencies: es-module-lexer "^1.5.3" slashes "^3.0.12" @@ -5906,7 +5911,7 @@ slice-ansi@^7.1.0: ansi-styles "^6.2.1" is-fullwidth-code-point "^5.0.0" -smol-toml@^1.1.4: +smol-toml@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/smol-toml/-/smol-toml-1.3.0.tgz#5200e251fffadbb72570c84e9776d2a3eca48143" integrity sha512-tWpi2TsODPScmi48b/OQZGi2lgUmBCHy6SZrhi/FdnnHiU1GwebbCfuQuxsC3nHaLwtYeJGPrDZDIeodDOc4pA==