From 05e1b7a12285eb5f1f3e24dde3d748dd201d235b Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Thu, 27 Jul 2023 23:05:36 +0100 Subject: [PATCH 01/19] Implement `CryptoApi.checkKeyBackup` --- spec/integ/crypto/megolm-backup.spec.ts | 43 ++++++- src/client.ts | 3 + src/crypto-api.ts | 13 +- src/crypto-api/keybackup.ts | 8 ++ src/crypto/index.ts | 15 +++ src/rust-crypto/backup.ts | 157 +++++++++++++++++++++++- src/rust-crypto/rust-crypto.ts | 29 ++++- 7 files changed, 250 insertions(+), 18 deletions(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index 283e621c137..1b2b54f0cca 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -176,7 +176,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe expect(event.getContent()).toEqual("testytest"); }); - oldBackendOnly("getActiveSessionBackupVersion() should give correct result", async function () { + it("getActiveSessionBackupVersion() should give correct result", async function () { // 404 means that there is no active backup fetchMock.get("express:/_matrix/client/v3/room_keys/version", 404); @@ -187,7 +187,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe // tell Alice to trust the dummy device that signed the backup await waitForDeviceList(); await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); - await aliceClient.checkKeyBackup(); + await aliceCrypto.checkKeyBackupAndEnable(); // At this point there is no backup let backupStatus: string | null; @@ -201,9 +201,9 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe overwriteRoutes: true, }); - const checked = await aliceClient.checkKeyBackup(); + const checked = await aliceCrypto.checkKeyBackupAndEnable(); expect(checked?.backupInfo?.version).toStrictEqual(unsignedBackup.version); - expect(checked?.trustInfo?.usable).toBeFalsy(); + expect(checked?.trustInfo?.trusted).toBeFalsy(); backupStatus = await aliceCrypto.getActiveSessionBackupVersion(); expect(backupStatus).toBeNull(); @@ -222,8 +222,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); }); - const validCheck = await aliceClient.checkKeyBackup(); - expect(validCheck?.trustInfo?.usable).toStrictEqual(true); + const validCheck = await aliceCrypto.checkKeyBackupAndEnable(); + expect(validCheck?.trustInfo?.trusted).toStrictEqual(true); await backupPromise; @@ -286,6 +286,37 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); }); + describe("checkKeyBackupAndEnable", () => { + it("enables a backup signed by a trusted device", async () => { + aliceClient = await initTestClient(); + const aliceCrypto = aliceClient.getCrypto()!; + + // tell Alice to trust the dummy device that signed the backup + await aliceClient.startClient(); + await waitForDeviceList(); + await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result?.trustInfo).toEqual({ trusted: true, matchesDecryptionKey: false }); + expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version); + }); + + it("does not enable a backup signed by an untrusted device", async () => { + aliceClient = await initTestClient(); + const aliceCrypto = aliceClient.getCrypto()!; + + // download the device list, to match the trusted case + await aliceClient.startClient(); + await waitForDeviceList(); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result?.trustInfo).toEqual({ trusted: false, matchesDecryptionKey: false }); + expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull(); + }); + }); + /** make sure that the client knows about the dummy device */ async function waitForDeviceList(): Promise { // Completing the initial sync will make the device list download outdated device lists (of which our own diff --git a/src/client.ts b/src/client.ts index f50a1c74058..66852f3e1b4 100644 --- a/src/client.ts +++ b/src/client.ts @@ -2248,6 +2248,7 @@ export class MatrixClient extends TypedEventEmitter { if (!this.crypto) { diff --git a/src/crypto-api.ts b/src/crypto-api.ts index 40c45c83ddf..f4714fca478 100644 --- a/src/crypto-api.ts +++ b/src/crypto-api.ts @@ -20,7 +20,7 @@ import { DeviceMap } from "./models/device"; import { UIAuthCallback } from "./interactive-auth"; import { AddSecretStorageKeyOpts, SecretStorageCallbacks, SecretStorageKeyDescription } from "./secret-storage"; import { VerificationRequest } from "./crypto-api/verification"; -import { BackupTrustInfo, KeyBackupInfo } from "./crypto-api/keybackup"; +import { BackupTrustInfo, KeyBackupCheck, KeyBackupInfo } from "./crypto-api/keybackup"; import { ISignatures } from "./@types/signed"; /** @@ -339,6 +339,17 @@ export interface CryptoApi { * @param info - key backup info dict from {@link MatrixClient#getKeyBackupVersion}. */ isKeyBackupTrusted(info: KeyBackupInfo): Promise; + + /** + * Force a re-check of the key backup and enable/disable it as appropriate. + * + * Fetches the current backup information from the server. If there is a backup, and it is trusted, starts + * backing up to it; otherwise, disables backups. + * + * @returns `null` if there is no backup on the server. Otherwise, data on the backup as returned by the server, + * and trust information (as returned by {@link isKeyBackupTrusted}). + */ + checkKeyBackupAndEnable(): Promise; } /** diff --git a/src/crypto-api/keybackup.ts b/src/crypto-api/keybackup.ts index d741f5e04b4..9187d896405 100644 --- a/src/crypto-api/keybackup.ts +++ b/src/crypto-api/keybackup.ts @@ -60,3 +60,11 @@ export interface BackupTrustInfo { */ readonly matchesDecryptionKey: boolean; } + +/** + * The result of {@link CryptoApi.checkKeyBackupAndEnable}. + */ +export interface KeyBackupCheck { + backupInfo: KeyBackupInfo; + trustInfo: BackupTrustInfo; +} diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 1716e03f562..0562fbe6e11 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -92,6 +92,7 @@ import { CrossSigningStatus, DeviceVerificationStatus, ImportRoomKeysOpts, + KeyBackupCheck, KeyBackupInfo, VerificationRequest as CryptoApiVerificationRequest, } from "../crypto-api"; @@ -1304,6 +1305,20 @@ export class Crypto extends TypedEventEmitter { + const checkResult = await this.backupManager.checkKeyBackup(); + if (!checkResult || !checkResult.backupInfo) return null; + return { + backupInfo: checkResult.backupInfo, + trustInfo: backupTrustInfoFromLegacyTrustInfo(checkResult.trustInfo), + }; + } + /** * Checks that a given cross-signing private key matches a given public key. * This can be used by the getCrossSigningKey callback to verify that the diff --git a/src/rust-crypto/backup.ts b/src/rust-crypto/backup.ts index b5e2a2c61a0..c3a790ffa27 100644 --- a/src/rust-crypto/backup.ts +++ b/src/rust-crypto/backup.ts @@ -17,20 +17,33 @@ limitations under the License. import { OlmMachine, SignatureVerification } from "@matrix-org/matrix-sdk-crypto-wasm"; import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm"; -import { BackupTrustInfo, Curve25519AuthData, KeyBackupInfo } from "../crypto-api/keybackup"; +import { BackupTrustInfo, Curve25519AuthData, KeyBackupCheck, KeyBackupInfo } from "../crypto-api/keybackup"; +import { logger } from "../logger"; +import { ClientPrefix, IHttpOpts, MatrixError, MatrixHttpApi, Method } from "../http-api"; +import { CryptoEvent } from "../crypto"; +import { TypedEventEmitter } from "../models/typed-event-emitter"; /** * @internal */ -export class RustBackupManager { - public constructor(private readonly olmMachine: OlmMachine) {} +export class RustBackupManager extends TypedEventEmitter { + /** Have we checked if there is a backup on the server which we can use */ + private checkedForBackup = false; + private activeBackupVersion: string | null = null; + + public constructor( + private readonly olmMachine: OlmMachine, + private readonly http: MatrixHttpApi, + ) { + super(); + } /** * Get the backup version we are currently backing up to, if any */ public async getActiveBackupVersion(): Promise { - // TODO stub - return null; + if (!this.olmMachine.isBackupEnabled()) return null; + return this.activeBackupVersion; } /** @@ -52,4 +65,138 @@ export class RustBackupManager { trusted: signatureVerification.trusted(), }; } + + /** + * Re-check the key backup and enable/disable it as appropriate. + * + * @param force - whether we should force a re-check even if one has already happened. + */ + public checkKeyBackupAndEnable(force: boolean): Promise { + if (!force && this.checkedForBackup) { + return Promise.resolve(null); + } + + // make sure there is only one check going on at a time + if (!this.keyBackupCheckInProgress) { + this.keyBackupCheckInProgress = this.doCheckKeyBackup().finally(() => { + this.keyBackupCheckInProgress = null; + }); + } + return this.keyBackupCheckInProgress; + } + private keyBackupCheckInProgress: Promise | null = null; + + /** Helper for `checkKeyBackup` */ + private async doCheckKeyBackup(): Promise { + logger.log("Checking key backup status..."); + let backupInfo: KeyBackupInfo | null = null; + try { + backupInfo = await this.requestKeyBackupVersion(); + } catch (e) { + logger.warn("Error checking for active key backup", e); + return null; + } + this.checkedForBackup = true; + + if (backupInfo && !backupInfo.version) { + logger.warn("active backup lacks a useful 'version'; ignoring it"); + } + + const activeVersion = await this.getActiveBackupVersion(); + + if (!backupInfo) { + if (activeVersion !== null) { + logger.log("No key backup present on server: disabling key backup"); + await this.disableKeyBackup(); + } else { + logger.log("No key backup present on server: not enabling key backup"); + } + return null; + } + + const trustInfo = await this.isKeyBackupTrusted(backupInfo); + + if (!trustInfo.trusted) { + if (activeVersion !== null) { + logger.log("Key backup present on server but not trusted: disabling key backup"); + await this.disableKeyBackup(); + } else { + logger.log("Key backup present on server but not trusted: not enabling key backup"); + } + } else { + if (activeVersion === null) { + logger.log(`Found usable key backup v${backupInfo.version}: enabling key backups`); + await this.enableKeyBackup(backupInfo); + } else if (activeVersion !== backupInfo.version) { + logger.log(`On backup version ${activeVersion} but found version ${backupInfo.version}: switching.`); + await this.disableKeyBackup(); + await this.enableKeyBackup(backupInfo); + // We're now using a new backup, so schedule all the keys we have to be + // uploaded to the new backup. This is a bit of a workaround to upload + // keys to a new backup in *most* cases, but it won't cover all cases + // because we don't remember what backup version we uploaded keys to: + // see https://github.com/vector-im/element-web/issues/14833 + await this.scheduleAllGroupSessionsForBackup(); + } else { + logger.log(`Backup version ${backupInfo.version} still current`); + } + } + return { backupInfo, trustInfo }; + } + + private async enableKeyBackup(backupInfo: KeyBackupInfo): Promise { + // we know for certain it must be a Curve25519 key, because we have verified it and only Curve25519 + // keys can be verified. + // + // we also checked it has a valid `version`. + await this.olmMachine.enableBackupV1( + (backupInfo.auth_data as Curve25519AuthData).public_key, + backupInfo.version!, + ); + this.activeBackupVersion = backupInfo.version!; + + this.emit(CryptoEvent.KeyBackupStatus, true); + + // TODO: kick off an upload loop + } + + private async disableKeyBackup(): Promise { + await this.olmMachine.disableBackup(); + this.activeBackupVersion = null; + this.emit(CryptoEvent.KeyBackupStatus, false); + } + + private async scheduleAllGroupSessionsForBackup(): Promise { + // TODO stub + } + /** + * Get information about the current key backup from the server + * + * @returns Information object from API or null if there is no active backup. + */ + private async requestKeyBackupVersion(): Promise { + try { + return await this.http.authedRequest( + Method.Get, + "/room_keys/version", + undefined, + undefined, + { + prefix: ClientPrefix.V3, + }, + ); + } catch (e) { + if ((e).errcode === "M_NOT_FOUND") { + return null; + } else { + throw e; + } + } + } } + +export type RustBackupCryptoEvents = CryptoEvent.KeyBackupStatus; + +export type RustBackupCryptoEventMap = { + [CryptoEvent.KeyBackupStatus]: (enabled: boolean) => void; +}; diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts index e79499899e8..4e596266ea7 100644 --- a/src/rust-crypto/rust-crypto.ts +++ b/src/rust-crypto/rust-crypto.ts @@ -35,15 +35,16 @@ import { BootstrapCrossSigningOpts, CreateSecretStorageOpts, CrossSigningKey, + CrossSigningKeyInfo, CrossSigningStatus, CryptoCallbacks, DeviceVerificationStatus, GeneratedSecretStorageKey, ImportRoomKeyProgressData, ImportRoomKeysOpts, + KeyBackupCheck, KeyBackupInfo, VerificationRequest, - CrossSigningKeyInfo, } from "../crypto-api"; import { deviceKeysToDeviceMap, rustDeviceToJsDevice } from "./device-converter"; import { IDownloadKeyResult, IQueryKeysRequest } from "../client"; @@ -58,7 +59,8 @@ import { RustVerificationRequest, verificationMethodIdentifierToMethod } from ". import { EventType } from "../@types/event"; import { CryptoEvent } from "../crypto"; import { TypedEventEmitter } from "../models/typed-event-emitter"; -import { RustBackupManager } from "./backup"; +import { RustBackupCryptoEventMap, RustBackupCryptoEvents, RustBackupManager } from "./backup"; +import { TypedReEmitter } from "../ReEmitter"; const ALL_VERIFICATION_METHODS = ["m.sas.v1", "m.qr_code.scan.v1", "m.qr_code.show.v1", "m.reciprocate.v1"]; @@ -84,8 +86,9 @@ export class RustCrypto extends TypedEventEmitter(this); public constructor( /** The `OlmMachine` from the underlying rust crypto sdk. */ @@ -114,7 +117,9 @@ export class RustCrypto extends TypedEventEmitter { @@ -804,6 +809,15 @@ export class RustCrypto extends TypedEventEmitter { + return await this.backupManager.checkKeyBackupAndEnable(true); + } + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // // SyncCryptoCallbacks implementation @@ -1149,7 +1163,10 @@ class EventDecryptor { } } -type RustCryptoEvents = CryptoEvent.VerificationRequestReceived | CryptoEvent.UserTrustStatusChanged; +type RustCryptoEvents = + | CryptoEvent.VerificationRequestReceived + | CryptoEvent.UserTrustStatusChanged + | RustBackupCryptoEvents; type RustCryptoEventMap = { /** @@ -1161,4 +1178,4 @@ type RustCryptoEventMap = { * Fires when the cross signing keys are imported during {@link CryptoApi#bootstrapCrossSigning} */ [CryptoEvent.UserTrustStatusChanged]: (userId: string, userTrustLevel: UserTrustLevel) => void; -}; +} & RustBackupCryptoEventMap; From d840e90c6a022ef76edb49b304b32774baecb8be Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 28 Jul 2023 11:26:08 +0100 Subject: [PATCH 02/19] Deprecate `MatrixClient.enableKeyBackup`. --- src/client.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/client.ts b/src/client.ts index 66852f3e1b4..2f1a882e970 100644 --- a/src/client.ts +++ b/src/client.ts @@ -3249,7 +3249,7 @@ export class MatrixClient extends TypedEventEmitter { if (!this.crypto) { @@ -3319,6 +3319,8 @@ export class MatrixClient extends TypedEventEmitter { if (!this.crypto) { From 39e24bf085be0319941e68e94d3bc40a92cd76f4 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Fri, 28 Jul 2023 15:28:25 +0100 Subject: [PATCH 03/19] fix integ test --- spec/integ/crypto/megolm-backup.spec.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index 1b2b54f0cca..a57b125f8d0 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -299,7 +299,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); const result = await aliceCrypto.checkKeyBackupAndEnable(); - expect(result?.trustInfo).toEqual({ trusted: true, matchesDecryptionKey: false }); + expect(result).toBeTruthy(); + expect(result!.trustInfo).toEqual({ trusted: true, matchesDecryptionKey: false }); expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version); }); @@ -311,8 +312,11 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await aliceClient.startClient(); await waitForDeviceList(); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); + const result = await aliceCrypto.checkKeyBackupAndEnable(); - expect(result?.trustInfo).toEqual({ trusted: false, matchesDecryptionKey: false }); + expect(result).toBeTruthy(); + expect(result!.trustInfo).toEqual({ trusted: false, matchesDecryptionKey: false }); expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull(); }); }); From bf40a023561684984c5c938d6646989ba720b436 Mon Sep 17 00:00:00 2001 From: valere Date: Wed, 2 Aug 2023 10:23:53 +0200 Subject: [PATCH 04/19] more tests --- spec/integ/crypto/megolm-backup.spec.ts | 87 +++++++++++++++++++++++++ spec/test-utils/test-data/index.ts | 2 +- 2 files changed, 88 insertions(+), 1 deletion(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index a57b125f8d0..b312f9d803b 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -319,6 +319,93 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe expect(result!.trustInfo).toEqual({ trusted: false, matchesDecryptionKey: false }); expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull(); }); + + it("disables backup when a new untrusted backup is available", async () => { + aliceClient = await initTestClient(); + const aliceCrypto = aliceClient.getCrypto()!; + + // tell Alice to trust the dummy device that signed the backup + await aliceClient.startClient(); + await waitForDeviceList(); + await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result).toBeTruthy(); + expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version); + + const unsignedBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA)); + delete unsignedBackup.auth_data.signatures; + unsignedBackup.version = "2"; + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", unsignedBackup, { + overwriteRoutes: true, + }); + + await aliceCrypto.checkKeyBackupAndEnable(); + expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull(); + }); + + it("switches backup when a new trusted backup is available", async () => { + aliceClient = await initTestClient(); + const aliceCrypto = aliceClient.getCrypto()!; + + // tell Alice to trust the dummy device that signed the backup + await aliceClient.startClient(); + await waitForDeviceList(); + await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result).toBeTruthy(); + expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version); + + const newBackupVersion = "2"; + const unsignedBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA)); + unsignedBackup.version = newBackupVersion; + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", unsignedBackup, { + overwriteRoutes: true, + }); + + await aliceCrypto.checkKeyBackupAndEnable(); + expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(newBackupVersion); + }); + + it("Disables when backup is deleted", async () => { + aliceClient = await initTestClient(); + const aliceCrypto = aliceClient.getCrypto()!; + + // tell Alice to trust the dummy device that signed the backup + await aliceClient.startClient(); + await waitForDeviceList(); + await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result).toBeTruthy(); + expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version); + + fetchMock.get( + "path:/_matrix/client/v3/room_keys/version", + { + status: 404, + body: { + errcode: "M_NOT_FOUND", + error: "No backup found", + }, + }, + { + overwriteRoutes: true, + }, + ); + const noResult = await aliceCrypto.checkKeyBackupAndEnable(); + expect(noResult).toBeNull(); + expect(await aliceCrypto.getActiveSessionBackupVersion()).toBeNull(); + }); }); /** make sure that the client knows about the dummy device */ diff --git a/spec/test-utils/test-data/index.ts b/spec/test-utils/test-data/index.ts index 6b904e62d8a..3e3243c819f 100644 --- a/spec/test-utils/test-data/index.ts +++ b/spec/test-utils/test-data/index.ts @@ -114,4 +114,4 @@ export const SIGNED_BACKUP_DATA: KeyBackupInfo = { } } } -}; +}; \ No newline at end of file From 84858467319cd67fb23b6c162c61e14659959060 Mon Sep 17 00:00:00 2001 From: valere Date: Mon, 7 Aug 2023 17:16:36 +0200 Subject: [PATCH 05/19] Implement keybackup loop --- spec/integ/crypto/megolm-backup.spec.ts | 324 +++++++++++++++++++- spec/test-utils/test-data/index.ts | 42 ++- spec/unit/rust-crypto/rust-crypto.spec.ts | 31 +- src/client.ts | 2 + src/crypto/backup.ts | 8 + src/rust-crypto/OutgoingRequestProcessor.ts | 7 +- src/rust-crypto/backup.ts | 112 ++++++- src/rust-crypto/rust-crypto.ts | 11 +- 8 files changed, 491 insertions(+), 46 deletions(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index b312f9d803b..1f218cc6d72 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -17,9 +17,10 @@ limitations under the License. import fetchMock from "fetch-mock-jest"; import "fake-indexeddb/auto"; import { IDBFactory } from "fake-indexeddb"; +import EventEmitter from "events"; import { IKeyBackupSession } from "../../../src/crypto/keybackup"; -import { createClient, CryptoEvent, ICreateClientOpts, IEvent, MatrixClient } from "../../../src"; +import { createClient, CryptoEvent, ICreateClientOpts, IEvent, MatrixClient, TypedEventEmitter } from "../../../src"; import { SyncResponder } from "../../test-utils/SyncResponder"; import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver"; import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder"; @@ -27,6 +28,8 @@ import { mockInitialApiRequests } from "../../test-utils/mockEndpoints"; import { awaitDecryption, CRYPTO_BACKENDS, InitCrypto, syncPromise } from "../../test-utils/test-utils"; import * as testData from "../../test-utils/test-data"; import { KeyBackupInfo } from "../../../src/crypto-api/keybackup"; +import { IKeyBackup } from "../../../src/crypto/backup"; +import { sleep } from "../../../src/utils"; const ROOM_ID = "!ROOM:ID"; @@ -83,6 +86,58 @@ afterEach(() => { indexedDB = new IDBFactory(); }); +enum MockKeyUploadEvent { + KeyUploaded = "KeyUploaded", +} + +type MockKeyUploadEventHandlerMap = { + [MockKeyUploadEvent.KeyUploaded]: (roomId: string, sessionId: string, backupVersion: string) => void; +}; + +/* + * Test helper. Will emit an event everytime fetchmock sees a request to backup a key. + */ +function mockUploadEmitter( + expectedVersion: string, +): TypedEventEmitter { + const emitter = new TypedEventEmitter(); + fetchMock.put( + "path:/_matrix/client/v3/room_keys/keys", + (url, request) => { + const version = new URLSearchParams(new URL(url).search).get("version"); + if (version != expectedVersion) { + return { + status: 403, + body: { + current_version: expectedVersion, + errcode: "M_WRONG_ROOM_KEYS_VERSION", + error: "Wrong backup version.", + }, + }; + } + const uploadPayload: IKeyBackup = JSON.parse(request.body?.toString() ?? "{}"); + let count = 0; + for (const [roomId, value] of Object.entries(uploadPayload.rooms)) { + for (const sessionId of Object.keys(value.sessions)) { + emitter.emit(MockKeyUploadEvent.KeyUploaded, roomId, sessionId, version); + count++; + } + } + return { + status: 200, + body: { + count: count, + etag: "abcdefg", + }, + }; + }, + { + overwriteRoutes: true, + }, + ); + return emitter; +} + describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backend: string, initCrypto: InitCrypto) => { // oldBackendOnly is an alternative to `it` or `test` which will skip the test if we are running against the // Rust backend. Once we have full support in the rust sdk, it will go away. @@ -176,6 +231,267 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe expect(event.getContent()).toEqual("testytest"); }); + describe("backupLoop", () => { + it("Alice should upload known keys when backup is enabled", async function () { + // 404 means that there is no active backup + fetchMock.get("path:/_matrix/client/v3/room_keys/version", 404); + + aliceClient = await initTestClient(); + const aliceCrypto = aliceClient.getCrypto()!; + await aliceClient.startClient(); + + // tell Alice to trust the dummy device that signed the backup + await waitForDeviceList(); + await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); + + // check that signalling is working + const remainingZeroPromise = new Promise((resolve, reject) => { + aliceClient.on(CryptoEvent.KeyBackupSessionsRemaining, (remaining) => { + if (remaining == 0) { + resolve(); + } + }); + }); + + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; + + const uploadMockEmitter = mockUploadEmitter(testData.SIGNED_BACKUP_DATA.version!); + + const uploadPromises = someRoomKeys.map((data) => { + new Promise((resolve) => { + uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => { + if ( + data.room_id == roomId && + data.session_id == sessionId && + version == testData.SIGNED_BACKUP_DATA.version + ) { + resolve(); + } + }); + }); + }); + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { + overwriteRoutes: true, + }); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result).toBeTruthy(); + + await aliceCrypto.importRoomKeys(someRoomKeys); + + // The backup loop is waiting a random amount of time to avoid different clients firing at the same time. + jest.runAllTimers(); + + await Promise.all(uploadPromises); + + // Wait until all keys are backed up to ensure that when a new key is received the loop is restarted + await remainingZeroPromise; + + // A new key import should trigger a new upload. + const newKey = testData.MEGOLM_SESSION_DATA; + + const newKeyUploadPromise = new Promise((resolve) => { + uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => { + if ( + newKey.room_id == roomId && + newKey.session_id == sessionId && + version == testData.SIGNED_BACKUP_DATA.version + ) { + resolve(); + } + }); + }); + + await aliceCrypto.importRoomKeys([newKey]); + + jest.runAllTimers(); + await newKeyUploadPromise; + }); + + it("Alice should re-upload all keys if a new trusted backup is available", async function () { + aliceClient = await initTestClient(); + const aliceCrypto = aliceClient.getCrypto()!; + await aliceClient.startClient(); + + // tell Alice to trust the dummy device that signed the backup + await waitForDeviceList(); + await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); + + // check that signalling is working + const remainingZeroPromise = new Promise((resolve) => { + aliceClient.on(CryptoEvent.KeyBackupSessionsRemaining, (remaining) => { + if (remaining == 0) { + resolve(); + } + }); + }); + + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { + overwriteRoutes: true, + }); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result).toBeTruthy(); + + mockUploadEmitter(testData.SIGNED_BACKUP_DATA.version!); + await aliceCrypto.importRoomKeys(someRoomKeys); + + // The backup loop is waiting a random amount of time to avoid different clients firing at the same time. + jest.runAllTimers(); + + // wait for all keys to be backed up + await remainingZeroPromise; + + const newBackupVersion = "2"; + const uploadMockEmitter = mockUploadEmitter(newBackupVersion); + const newBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA)); + newBackup.version = newBackupVersion; + + // Let's simulate that a new backup is available by returning error code on key upload + + fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup, { + overwriteRoutes: true, + }); + + // If we import a new key the loop will try to upload to old version, it will + // fail then check the current version and switch if trusted + const uploadPromises = someRoomKeys.map((data) => { + new Promise((resolve) => { + uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => { + if (data.room_id == roomId && data.session_id == sessionId && version == newBackupVersion) { + resolve(); + } + }); + }); + }); + + const disableOldBackup = new Promise((resolve) => { + aliceClient.on(CryptoEvent.KeyBackupFailed, (errCode) => { + if (errCode == "M_WRONG_ROOM_KEYS_VERSION") { + resolve(); + } + }); + }); + + const enableNewBackup = new Promise((resolve) => { + aliceClient.on(CryptoEvent.KeyBackupStatus, (enabled) => { + if (enabled) { + resolve(); + } + }); + }); + + // A new key import should trigger a new upload. + const newKey = testData.MEGOLM_SESSION_DATA; + + const newKeyUploadPromise = new Promise((resolve) => { + uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => { + if (newKey.room_id == roomId && newKey.session_id == sessionId && version == newBackupVersion) { + resolve(); + } + }); + }); + + await aliceCrypto.importRoomKeys([newKey]); + + jest.runAllTimers(); + + await disableOldBackup; + await enableNewBackup; + + jest.runAllTimers(); + + await Promise.all(uploadPromises); + await newKeyUploadPromise; + }); + + it("Backup loop should resist to network failures", async function () { + aliceClient = await initTestClient(); + const aliceCrypto = aliceClient.getCrypto()!; + await aliceClient.startClient(); + + // tell Alice to trust the dummy device that signed the backup + await waitForDeviceList(); + await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); + + const failureTestEmitter = new EventEmitter(); + let networkFailureCount = 0; + + // Simulate network failures on keys upload, then a succesfull upload + fetchMock.put( + "path:/_matrix/client/v3/room_keys/keys", + () => { + if (networkFailureCount < 1) { + networkFailureCount++; + failureTestEmitter.emit("NetworkFailure"); + throw TypeError(`Failed to fetch`); + } + failureTestEmitter.emit("NetworkResolved"); + return { + status: 200, + body: { + count: 2, + etag: "abcdefg", + }, + }; + }, + { + overwriteRoutes: true, + }, + ); + fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { + overwriteRoutes: true, + }); + + const failurePrmoise = new Promise((resolve) => { + failureTestEmitter.on("NetworkFailure", () => { + // Can't just use a fake time here, if I use a fake one + // the backoff timer of the loop won't be created yet thus the runAllTimers will do nothing. + // not sure how to do better here + jest.useRealTimers(); + setTimeout(resolve, 200); + jest.useFakeTimers(); + }); + }); + + const resolvePrmoise = new Promise((resolve) => { + failureTestEmitter.on("NetworkResolved", () => { + // Can't just use a fake time here, if I use a fake one + // the backoff timer of the loop won't be created yet thus the runAllTimers will do nothing. + // not sure how to do better here + jest.useRealTimers(); + setTimeout(resolve, 200); + jest.useFakeTimers(); + }); + }); + + const allKeyUploadedPromise = new Promise((resolve) => { + aliceClient.on(CryptoEvent.KeyBackupSessionsRemaining, (remaining) => { + if (remaining == 0) { + resolve(); + } + }); + }); + + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; + await aliceCrypto.importRoomKeys(someRoomKeys); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result).toBeTruthy(); + jest.runAllTimers(); + + await failurePrmoise; + jest.runAllTimers(); + await resolvePrmoise; + jest.runAllTimers(); + + await allKeyUploadedPromise; + }); + }); + it("getActiveSessionBackupVersion() should give correct result", async function () { // 404 means that there is no active backup fetchMock.get("express:/_matrix/client/v3/room_keys/version", 404); @@ -363,10 +679,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe expect(await aliceCrypto.getActiveSessionBackupVersion()).toEqual(testData.SIGNED_BACKUP_DATA.version); const newBackupVersion = "2"; - const unsignedBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA)); - unsignedBackup.version = newBackupVersion; + const newBackup = JSON.parse(JSON.stringify(testData.SIGNED_BACKUP_DATA)); + newBackup.version = newBackupVersion; - fetchMock.get("path:/_matrix/client/v3/room_keys/version", unsignedBackup, { + fetchMock.get("path:/_matrix/client/v3/room_keys/version", newBackup, { overwriteRoutes: true, }); diff --git a/spec/test-utils/test-data/index.ts b/spec/test-utils/test-data/index.ts index 5a2e3fa9d68..88c7d54a5ae 100644 --- a/spec/test-utils/test-data/index.ts +++ b/spec/test-utils/test-data/index.ts @@ -3,7 +3,7 @@ * Do not edit by hand! This file is generated by `./generate-test-data.py` */ -import { IDeviceKeys } from "../../../src/@types/crypto"; +import { IDeviceKeys, IMegolmSessionData } from "../../../src/@types/crypto"; import { IDownloadKeyResult } from "../../../src"; import { KeyBackupInfo } from "../../../src/crypto-api"; @@ -115,4 +115,44 @@ export const SIGNED_BACKUP_DATA: KeyBackupInfo = { } } } +}; + +export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [ + { + algorithm: "m.megolm.v1.aes-sha2", + room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480", + sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY", + session_id: "iGQG5GaP1/B3dSH6zCQDQqrNuotrtQjVC7w1OsUDwbg", + session_key: + "AQAAAADaCbP2gdOy8jrhikjploKgSBaFSJ5rvHcziaADbwNEzeCSrfuAUlXvCvxik8kU+MfCHIi5arN2M7UM5rGKdzkHnkReoIByFkeMdbjKWk5SFpVQexcM74eDhBGj+ICkQqOgApfnEbSswrmreB0+MhHHyLStwW5fy5f8A9QW1sbPuohkBuRmj9fwd3Uh+swkA0KqzbqLa7UI1Qu8NTrFA8G4", + sender_claimed_keys: { + ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k", + }, + forwarding_curve25519_key_chain: [], + }, + { + algorithm: "m.megolm.v1.aes-sha2", + room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480", + sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY", + session_id: "P/Jy9Tog4CMtLseeS4Fe2AEXZov3k6cibcop/uyhr78", + session_key: + "AQAAAAATyAVm0c9c9DW9Od72MxvfSDYoysBw3C6yMJ3bYuTmssHN7yNGm59KCtKeFp2Y5qO7lvUmwOfSTvTASUb7HViE7Lt+Bvp5WiMTJ2Pv6m+N12ihyowV5lgtKFWI18Wxd0AugMTVQRwjBK6aMobf86NXWD2hiKm3N6kWbC0PXmqV7T/ycvU6IOAjLS7HnkuBXtgBF2aL95OnIm3KKf7soa+/", + sender_claimed_keys: { + ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k", + }, + forwarding_curve25519_key_chain: [], + }, +]; + +export const MEGOLM_SESSION_DATA: IMegolmSessionData = { + algorithm: "m.megolm.v1.aes-sha2", + sender_key: "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", + sender_claimed_keys: { + "ed25519": "vEjK0zoE/flKjhAbzzOePv5AWXUjzcfkKdTjLpyKuhI" + }, + room_id: "!EclSXmduSDvVcwKLnn:example.org", + session_id: "QYiBwpB7nf0lcLMR7T9DPaQA5ml4Mhh0WfjAWrDWyOY", + session_key: "AQAAAAAkbP0ON4cmpKBZuUXBDlfz0iSowKc/9FYzWIQVmdQM3tk5YDUaO+9kmp0T00XImkgbKCq2CFcoaQAITVU5qqNTi04P9ntakp6A3RoLn7VCiJd9RPfwaiSMeKQakGyFotkXkaiUKcPCHgEslVo1SmnaEcKLuxn9gEueHCK3hYvwTUGIgcKQe539JXCzEe0/Qz2kAOZpeDIYdFn4wFqw1sjm", + forwarding_curve25519_key_chain: [], + first_known_index: 0, }; \ No newline at end of file diff --git a/spec/unit/rust-crypto/rust-crypto.spec.ts b/spec/unit/rust-crypto/rust-crypto.spec.ts index d306166de90..0985256731e 100644 --- a/spec/unit/rust-crypto/rust-crypto.spec.ts +++ b/spec/unit/rust-crypto/rust-crypto.spec.ts @@ -61,32 +61,7 @@ describe("RustCrypto", () => { ); it("should import and export keys", async () => { - const someRoomKeys = [ - { - algorithm: "m.megolm.v1.aes-sha2", - room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480", - sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY", - session_id: "iGQG5GaP1/B3dSH6zCQDQqrNuotrtQjVC7w1OsUDwbg", - session_key: - "AQAAAADaCbP2gdOy8jrhikjploKgSBaFSJ5rvHcziaADbwNEzeCSrfuAUlXvCvxik8kU+MfCHIi5arN2M7UM5rGKdzkHnkReoIByFkeMdbjKWk5SFpVQexcM74eDhBGj+ICkQqOgApfnEbSswrmreB0+MhHHyLStwW5fy5f8A9QW1sbPuohkBuRmj9fwd3Uh+swkA0KqzbqLa7UI1Qu8NTrFA8G4", - sender_claimed_keys: { - ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k", - }, - forwarding_curve25519_key_chain: [], - }, - { - algorithm: "m.megolm.v1.aes-sha2", - room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480", - sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY", - session_id: "P/Jy9Tog4CMtLseeS4Fe2AEXZov3k6cibcop/uyhr78", - session_key: - "AQAAAAATyAVm0c9c9DW9Od72MxvfSDYoysBw3C6yMJ3bYuTmssHN7yNGm59KCtKeFp2Y5qO7lvUmwOfSTvTASUb7HViE7Lt+Bvp5WiMTJ2Pv6m+N12ihyowV5lgtKFWI18Wxd0AugMTVQRwjBK6aMobf86NXWD2hiKm3N6kWbC0PXmqV7T/ycvU6IOAjLS7HnkuBXtgBF2aL95OnIm3KKf7soa+/", - sender_claimed_keys: { - ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k", - }, - forwarding_curve25519_key_chain: [], - }, - ]; + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; let importTotal = 0; const opt: ImportRoomKeysOpts = { progressCallback: (stage) => { @@ -95,11 +70,11 @@ describe("RustCrypto", () => { }; await rustCrypto.importRoomKeys(someRoomKeys, opt); - expect(importTotal).toBe(2); + expect(importTotal).toBe(someRoomKeys.length); const keys = await rustCrypto.exportRoomKeys(); expect(Array.isArray(keys)).toBeTruthy(); - expect(keys.length).toBe(2); + expect(keys.length).toBe(someRoomKeys.length); const aSession = someRoomKeys[0]; diff --git a/src/client.ts b/src/client.ts index 94d8530bf8a..0d06a27c809 100644 --- a/src/client.ts +++ b/src/client.ts @@ -2252,6 +2252,8 @@ export class MatrixClient extends TypedEventEmitter { + logger.debug(`Key backup: scheduleKeyBackupSend currentSending:${this.sendingBackups} delay:${maxDelay}`); if (this.sendingBackups) return; this.sendingBackups = true; @@ -474,6 +475,9 @@ export class BackupManager { (err).data.errcode == "M_NOT_FOUND" || (err).data.errcode == "M_WRONG_ROOM_KEYS_VERSION" ) { + // fix do it now as the await check might trigger a new call to the loop before + // the finally is called + this.sendingBackups = false; // Re-check key backup status on error, so we can be // sure to present the current situation when asked. await this.checkKeyBackup(); @@ -494,6 +498,10 @@ export class BackupManager { return; } } + } catch (err) { + // No one actually check errors on this promise, it's spawned internally. + // Just log, apps/client should use events to check status + logger.log(`Backup loop failed ${err}`); } finally { this.sendingBackups = false; } diff --git a/src/rust-crypto/OutgoingRequestProcessor.ts b/src/rust-crypto/OutgoingRequestProcessor.ts index c67bcd7c298..6a9848aa8d2 100644 --- a/src/rust-crypto/OutgoingRequestProcessor.ts +++ b/src/rust-crypto/OutgoingRequestProcessor.ts @@ -75,7 +75,12 @@ export class OutgoingRequestProcessor { } 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); + resp = await this.rawJsonRequest( + Method.Put, + "/_matrix/client/v3/room_keys/keys", + { version: msg.version }, + msg.body, + ); } else if (msg instanceof ToDeviceRequest) { const path = `/_matrix/client/v3/sendToDevice/${encodeURIComponent(msg.event_type)}/` + diff --git a/src/rust-crypto/backup.ts b/src/rust-crypto/backup.ts index c3a790ffa27..89e23ee49c8 100644 --- a/src/rust-crypto/backup.ts +++ b/src/rust-crypto/backup.ts @@ -22,6 +22,8 @@ import { logger } from "../logger"; import { ClientPrefix, IHttpOpts, MatrixError, MatrixHttpApi, Method } from "../http-api"; import { CryptoEvent } from "../crypto"; import { TypedEventEmitter } from "../models/typed-event-emitter"; +import { OutgoingRequestProcessor } from "./OutgoingRequestProcessor"; +import { sleep } from "../utils"; /** * @internal @@ -30,14 +32,26 @@ export class RustBackupManager extends TypedEventEmitter, + private readonly outgoingRequestProcessor: OutgoingRequestProcessor, ) { super(); } + /** + * Tell the RustBackupManager to immediately stop. + */ + public stop(): void { + this.stopped = true; + } + /** * Get the backup version we are currently backing up to, if any */ @@ -129,14 +143,10 @@ export class RustBackupManager extends TypedEventEmitter { + if (this.activeBackupVersion != null) { + this.backupKeysLoop(); + } } private async disableKeyBackup(): Promise { @@ -166,9 +187,75 @@ export class RustBackupManager extends TypedEventEmitter { - // TODO stub + private async backupKeysLoop(maxDelay = 10000): Promise { + if (this.backupKeysLoopRunning) { + logger.log(`Backup loop already running`); + return; + } + this.backupKeysLoopRunning = true; + + logger.log(`Starting loop for ${this.activeBackupVersion}.`); + + // wait between 0 and `maxDelay` seconds, to avoid backup + // requests from different clients hitting the server all at + // the same time when a new key is sent + const delay = Math.random() * maxDelay; + await sleep(delay); + + try { + let numFailures = 0; // number of consecutive network failures for exponential backoff + + while (!this.stopped) { + // Get a batch of room keys to upload + logger.log(`Before olm.backupRoomKeys()`); + const request: RustSdkCryptoJs.KeysBackupRequest | null = await this.olmMachine.backupRoomKeys(); + logger.log(`After olm.backupRoomKeys() ${request?.body}`); + + if (!request || this.stopped || !this.activeBackupVersion) { + logger.log(`Ending loop for ${this.activeBackupVersion}.`); + return; + } + + try { + await this.outgoingRequestProcessor.makeOutgoingRequest(request); + numFailures = 0; + + const keyCount: RustSdkCryptoJs.RoomKeyCounts = await this.olmMachine.roomKeyCounts(); + const remaining = keyCount.total - keyCount.backedUp; + this.emit(CryptoEvent.KeyBackupSessionsRemaining, remaining); + } catch (err) { + numFailures++; + logger.error("Error processing backup request for rust crypto-sdk", err); + if ((err).data) { + const errCode = (err).data.errcode; + if (errCode == "M_NOT_FOUND" || errCode == "M_WRONG_ROOM_KEYS_VERSION") { + await this.disableKeyBackup(); + this.emit(CryptoEvent.KeyBackupFailed, (err).data.errcode!); + // There was an active backup and we are out of sync with the server + // force a check server side + this.backupKeysLoopRunning = false; + this.checkKeyBackupAndEnable(true); + return; + } else if (errCode == "M_LIMIT_EXCEEDED") { + // wait for that and then continue? + const waitTime = (err).data.retry_after_ms; + if (waitTime > 0) { + sleep(waitTime); + continue; + } // else go to the normal backoff + } + } + + // Someo other error (mx, network, or CORS or invalid urls?) anyhow backoff + // exponential backoff if we have failures + await sleep(1000 * Math.pow(2, Math.min(numFailures - 1, 4))); + } + } + } finally { + this.backupKeysLoopRunning = false; + } } + /** * Get information about the current key backup from the server * @@ -195,8 +282,13 @@ export class RustBackupManager extends TypedEventEmitter void; + [CryptoEvent.KeyBackupSessionsRemaining]: (remaining: number) => void; + [CryptoEvent.KeyBackupFailed]: (errCode: string) => void; }; diff --git a/src/rust-crypto/rust-crypto.ts b/src/rust-crypto/rust-crypto.ts index 61749eefa3e..96718b1a397 100644 --- a/src/rust-crypto/rust-crypto.ts +++ b/src/rust-crypto/rust-crypto.ts @@ -118,8 +118,12 @@ export class RustCrypto extends TypedEventEmitter { @@ -148,6 +152,7 @@ export class RustCrypto extends TypedEventEmitter Date: Tue, 8 Aug 2023 13:28:34 +0200 Subject: [PATCH 06/19] cleaning --- spec/integ/crypto/megolm-backup.spec.ts | 1 - src/rust-crypto/backup.ts | 2 -- 2 files changed, 3 deletions(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index 1f218cc6d72..9224d9f231d 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -29,7 +29,6 @@ import { awaitDecryption, CRYPTO_BACKENDS, InitCrypto, syncPromise } from "../.. import * as testData from "../../test-utils/test-data"; import { KeyBackupInfo } from "../../../src/crypto-api/keybackup"; import { IKeyBackup } from "../../../src/crypto/backup"; -import { sleep } from "../../../src/utils"; const ROOM_ID = "!ROOM:ID"; diff --git a/src/rust-crypto/backup.ts b/src/rust-crypto/backup.ts index 89e23ee49c8..4d84d80e7b4 100644 --- a/src/rust-crypto/backup.ts +++ b/src/rust-crypto/backup.ts @@ -207,9 +207,7 @@ export class RustBackupManager extends TypedEventEmitter Date: Tue, 8 Aug 2023 14:53:50 +0200 Subject: [PATCH 07/19] update matrix-sdk-crypto-wasm to 1.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 21c9d0aaa7e..c528a2b64cb 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,7 @@ ], "dependencies": { "@babel/runtime": "^7.12.5", - "@matrix-org/matrix-sdk-crypto-wasm": "^1.2.0", + "@matrix-org/matrix-sdk-crypto-wasm": "^1.2.1", "another-json": "^0.2.0", "bs58": "^5.0.0", "content-type": "^1.0.4", From 7934464687e55afe2de0872cac5588052b26786e Mon Sep 17 00:00:00 2001 From: valere Date: Tue, 8 Aug 2023 17:23:30 +0200 Subject: [PATCH 08/19] fix lint --- spec/integ/crypto/megolm-backup.spec.ts | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index 9224d9f231d..c09f5eef754 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -17,7 +17,6 @@ limitations under the License. import fetchMock from "fetch-mock-jest"; import "fake-indexeddb/auto"; import { IDBFactory } from "fake-indexeddb"; -import EventEmitter from "events"; import { IKeyBackupSession } from "../../../src/crypto/keybackup"; import { createClient, CryptoEvent, ICreateClientOpts, IEvent, MatrixClient, TypedEventEmitter } from "../../../src"; @@ -416,7 +415,17 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await waitForDeviceList(); await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); - const failureTestEmitter = new EventEmitter(); + enum TestEmitterEvent { + NetworkFailure = "NetworkFailure", + NetworkResolved = "NetworkResolved", + } + + type TestEmitterEventHandlerMap = { + [TestEmitterEvent.NetworkFailure]: () => void; + [TestEmitterEvent.NetworkResolved]: () => void; + }; + + const failureTestEmitter = new TypedEventEmitter(); let networkFailureCount = 0; // Simulate network failures on keys upload, then a succesfull upload @@ -425,10 +434,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe () => { if (networkFailureCount < 1) { networkFailureCount++; - failureTestEmitter.emit("NetworkFailure"); - throw TypeError(`Failed to fetch`); + failureTestEmitter.emit(TestEmitterEvent.NetworkFailure); + throw new TypeError(`Failed to fetch`); } - failureTestEmitter.emit("NetworkResolved"); + failureTestEmitter.emit(TestEmitterEvent.NetworkResolved); return { status: 200, body: { @@ -446,7 +455,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); const failurePrmoise = new Promise((resolve) => { - failureTestEmitter.on("NetworkFailure", () => { + failureTestEmitter.on(TestEmitterEvent.NetworkFailure, () => { // Can't just use a fake time here, if I use a fake one // the backoff timer of the loop won't be created yet thus the runAllTimers will do nothing. // not sure how to do better here @@ -457,7 +466,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); const resolvePrmoise = new Promise((resolve) => { - failureTestEmitter.on("NetworkResolved", () => { + failureTestEmitter.on(TestEmitterEvent.NetworkResolved, () => { // Can't just use a fake time here, if I use a fake one // the backoff timer of the loop won't be created yet thus the runAllTimers will do nothing. // not sure how to do better here From a6d478f0f9307c38bd4d30ad91e29ebf6d85e218 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 9 Aug 2023 10:19:18 +0100 Subject: [PATCH 09/19] avoid real timer stuff --- spec/integ/crypto/megolm-backup.spec.ts | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index c09f5eef754..4484c2f3566 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -456,23 +456,13 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe const failurePrmoise = new Promise((resolve) => { failureTestEmitter.on(TestEmitterEvent.NetworkFailure, () => { - // Can't just use a fake time here, if I use a fake one - // the backoff timer of the loop won't be created yet thus the runAllTimers will do nothing. - // not sure how to do better here - jest.useRealTimers(); - setTimeout(resolve, 200); - jest.useFakeTimers(); + resolve(); }); }); const resolvePrmoise = new Promise((resolve) => { failureTestEmitter.on(TestEmitterEvent.NetworkResolved, () => { - // Can't just use a fake time here, if I use a fake one - // the backoff timer of the loop won't be created yet thus the runAllTimers will do nothing. - // not sure how to do better here - jest.useRealTimers(); - setTimeout(resolve, 200); - jest.useFakeTimers(); + resolve(); }); }); @@ -492,9 +482,9 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe jest.runAllTimers(); await failurePrmoise; - jest.runAllTimers(); + await jest.runAllTimersAsync(); await resolvePrmoise; - jest.runAllTimers(); + await jest.runAllTimersAsync(); await allKeyUploadedPromise; }); From 0345a76040c0e0c7151d491c4b9ccd58db21d976 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 9 Aug 2023 10:38:08 +0100 Subject: [PATCH 10/19] Simplify test --- spec/integ/crypto/megolm-backup.spec.ts | 104 +++++++++++------------- 1 file changed, 46 insertions(+), 58 deletions(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index 4484c2f3566..ab63637623d 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -406,7 +406,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await newKeyUploadPromise; }); - it("Backup loop should resist to network failures", async function () { + it("Backup loop should be resistant to network failures", async function () { aliceClient = await initTestClient(); const aliceCrypto = aliceClient.getCrypto()!; await aliceClient.startClient(); @@ -415,78 +415,66 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await waitForDeviceList(); await aliceCrypto.setDeviceVerified(testData.TEST_USER_ID, testData.TEST_DEVICE_ID); - enum TestEmitterEvent { - NetworkFailure = "NetworkFailure", - NetworkResolved = "NetworkResolved", - } - - type TestEmitterEventHandlerMap = { - [TestEmitterEvent.NetworkFailure]: () => void; - [TestEmitterEvent.NetworkResolved]: () => void; - }; - - const failureTestEmitter = new TypedEventEmitter(); - let networkFailureCount = 0; - - // Simulate network failures on keys upload, then a succesfull upload - fetchMock.put( - "path:/_matrix/client/v3/room_keys/keys", - () => { - if (networkFailureCount < 1) { - networkFailureCount++; - failureTestEmitter.emit(TestEmitterEvent.NetworkFailure); - throw new TypeError(`Failed to fetch`); - } - failureTestEmitter.emit(TestEmitterEvent.NetworkResolved); - return { - status: 200, - body: { - count: 2, - etag: "abcdefg", - }, - }; - }, - { - overwriteRoutes: true, - }, - ); fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { overwriteRoutes: true, }); - const failurePrmoise = new Promise((resolve) => { - failureTestEmitter.on(TestEmitterEvent.NetworkFailure, () => { - resolve(); - }); + // on the first key upload attempt, simulate a network failure + const failurePromise = new Promise((resolve) => { + fetchMock.put( + "path:/_matrix/client/v3/room_keys/keys", + () => { + resolve(undefined); + throw new TypeError(`Failed to fetch`); + }, + { + overwriteRoutes: true, + }, + ); }); - const resolvePrmoise = new Promise((resolve) => { - failureTestEmitter.on(TestEmitterEvent.NetworkResolved, () => { - resolve(); - }); + // kick the import loop off and wait for the failed request + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; + await aliceCrypto.importRoomKeys(someRoomKeys); + + const result = await aliceCrypto.checkKeyBackupAndEnable(); + expect(result).toBeTruthy(); + jest.runAllTimers(); + await failurePromise; + + // Fix the endpoint to do successful uploads + const successPromise = new Promise((resolve) => { + fetchMock.put( + "path:/_matrix/client/v3/room_keys/keys", + () => { + resolve(undefined); + return { + status: 200, + body: { + count: 2, + etag: "abcdefg", + }, + }; + }, + { + overwriteRoutes: true, + }, + ); }); - const allKeyUploadedPromise = new Promise((resolve) => { + // check that a `KeyBackupSessionsRemaining` event is emitted with `remaining == 0` + const allKeysUploadedPromise = new Promise((resolve) => { aliceClient.on(CryptoEvent.KeyBackupSessionsRemaining, (remaining) => { if (remaining == 0) { - resolve(); + resolve(undefined); } }); }); - const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; - await aliceCrypto.importRoomKeys(someRoomKeys); - - const result = await aliceCrypto.checkKeyBackupAndEnable(); - expect(result).toBeTruthy(); - jest.runAllTimers(); - - await failurePrmoise; + // run the timers, which will make the backup loop redo the request await jest.runAllTimersAsync(); - await resolvePrmoise; - await jest.runAllTimersAsync(); - - await allKeyUploadedPromise; + await successPromise; + await allKeysUploadedPromise; }); }); From 109e6d5ac3c1266a25ad03dea764b90ee91d7098 Mon Sep 17 00:00:00 2001 From: valere Date: Fri, 11 Aug 2023 17:29:47 +0200 Subject: [PATCH 11/19] post merge lint fix --- spec/test-utils/test-data/index.ts | 2 +- spec/test-utils/test-data/static-test-data.ts | 25 ++++++++++--------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/spec/test-utils/test-data/index.ts b/spec/test-utils/test-data/index.ts index 3b244844580..5a2e3fa9d68 100644 --- a/spec/test-utils/test-data/index.ts +++ b/spec/test-utils/test-data/index.ts @@ -3,7 +3,7 @@ * Do not edit by hand! This file is generated by `./generate-test-data.py` */ -import { IDeviceKeys, IMegolmSessionData } from "../../../src/@types/crypto"; +import { IDeviceKeys } from "../../../src/@types/crypto"; import { IDownloadKeyResult } from "../../../src"; import { KeyBackupInfo } from "../../../src/crypto-api"; diff --git a/spec/test-utils/test-data/static-test-data.ts b/spec/test-utils/test-data/static-test-data.ts index 25fa18c4ce8..89e94094846 100644 --- a/spec/test-utils/test-data/static-test-data.ts +++ b/spec/test-utils/test-data/static-test-data.ts @@ -1,4 +1,4 @@ -/* +/* * Static Test data for cryptography tests */ @@ -32,14 +32,15 @@ export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [ ]; export const MEGOLM_SESSION_DATA: IMegolmSessionData = { - algorithm: "m.megolm.v1.aes-sha2", - sender_key: "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", - sender_claimed_keys: { - "ed25519": "vEjK0zoE/flKjhAbzzOePv5AWXUjzcfkKdTjLpyKuhI" - }, - room_id: "!EclSXmduSDvVcwKLnn:example.org", - session_id: "QYiBwpB7nf0lcLMR7T9DPaQA5ml4Mhh0WfjAWrDWyOY", - session_key: "AQAAAAAkbP0ON4cmpKBZuUXBDlfz0iSowKc/9FYzWIQVmdQM3tk5YDUaO+9kmp0T00XImkgbKCq2CFcoaQAITVU5qqNTi04P9ntakp6A3RoLn7VCiJd9RPfwaiSMeKQakGyFotkXkaiUKcPCHgEslVo1SmnaEcKLuxn9gEueHCK3hYvwTUGIgcKQe539JXCzEe0/Qz2kAOZpeDIYdFn4wFqw1sjm", - forwarding_curve25519_key_chain: [], - first_known_index: 0, -}; \ No newline at end of file + algorithm: "m.megolm.v1.aes-sha2", + sender_key: "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", + sender_claimed_keys: { + ed25519: "vEjK0zoE/flKjhAbzzOePv5AWXUjzcfkKdTjLpyKuhI", + }, + room_id: "!EclSXmduSDvVcwKLnn:example.org", + session_id: "QYiBwpB7nf0lcLMR7T9DPaQA5ml4Mhh0WfjAWrDWyOY", + session_key: + "AQAAAAAkbP0ON4cmpKBZuUXBDlfz0iSowKc/9FYzWIQVmdQM3tk5YDUaO+9kmp0T00XImkgbKCq2CFcoaQAITVU5qqNTi04P9ntakp6A3RoLn7VCiJd9RPfwaiSMeKQakGyFotkXkaiUKcPCHgEslVo1SmnaEcKLuxn9gEueHCK3hYvwTUGIgcKQe539JXCzEe0/Qz2kAOZpeDIYdFn4wFqw1sjm", + forwarding_curve25519_key_chain: [], + first_known_index: 0, +}; From bada6a7c9863af4139fdd1ec94f1f07fd8bf7294 Mon Sep 17 00:00:00 2001 From: valere Date: Fri, 11 Aug 2023 17:48:34 +0200 Subject: [PATCH 12/19] revert change on yarn.lock --- yarn.lock | 548 ++++++++++++++++++++++++++---------------------------- 1 file changed, 260 insertions(+), 288 deletions(-) diff --git a/yarn.lock b/yarn.lock index 7087440bace..2fc39092e42 100644 --- a/yarn.lock +++ b/yarn.lock @@ -26,9 +26,9 @@ "@octokit/plugin-rest-endpoint-methods" "^5.13.0" "@actions/http-client@^2.0.1": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.1.0.tgz#b6d8c3934727d6a50d10d19f00a711a964599a9f" - integrity sha512-BonhODnXr3amchh4qkmjPMUO8mFi/zLaaCeCAJZqch8iQqyDnVIkySjB38VHAC8IJ+bnlgfOqlhpyCUZHlQsqw== + version "2.1.1" + resolved "https://registry.yarnpkg.com/@actions/http-client/-/http-client-2.1.1.tgz#a8e97699c315bed0ecaeaaeb640948470d4586a0" + integrity sha512-qhrkRMB40bbbLo7gF+0vu+X+UawOvQQqNAA/5Unx774RS8poaOhThDOG6BGmxvAnxhQnDp2BG/ZUm65xZILTpw== dependencies: tunnel "^0.0.6" @@ -41,9 +41,9 @@ "@jridgewell/trace-mapping" "^0.3.9" "@babel/cli@^7.12.10": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.9.tgz#501b3614aeda7399371f6d5991404f069b059986" - integrity sha512-nb2O7AThqRo7/E53EGiuAkMaRbb7J5Qp3RvN+dmua1U+kydm0oznkhqbTEG15yk26G/C3yL6OdZjzgl+DMXVVA== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.22.10.tgz#25e4bbd8d0a0d8b4b389e1b5e2d7a238bd4c1b75" + integrity sha512-rM9ZMmaII630zGvtMtQ3P4GyHs28CHLYE9apLG7L8TgaSqcfoIGrlLSLsh4Q8kDTdZQQEXZm1M0nQtOvU/2heg== dependencies: "@jridgewell/trace-mapping" "^0.3.17" commander "^4.0.1" @@ -56,12 +56,13 @@ "@nicolo-ribaudo/chokidar-2" "2.1.8-no-fsevents.3" chokidar "^3.4.0" -"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.5.tgz#234d98e1551960604f1246e6475891a570ad5658" - integrity sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ== +"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.22.10", "@babel/code-frame@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.22.10.tgz#1c20e612b768fefa75f6e90d6ecb86329247f0a3" + integrity sha512-/KKIMG4UEL35WmI9OlvMhurwtytjvXoFcGNrOvyG9zIzA8YmPjVtIZUf7b05+TPO7G7/GEmLHDaoCgACHl9hhA== dependencies: - "@babel/highlight" "^7.22.5" + "@babel/highlight" "^7.22.10" + chalk "^2.4.2" "@babel/compat-data@^7.20.5", "@babel/compat-data@^7.22.5", "@babel/compat-data@^7.22.6", "@babel/compat-data@^7.22.9": version "7.22.9" @@ -69,20 +70,20 @@ integrity sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ== "@babel/core@^7.0.0", "@babel/core@^7.11.6", "@babel/core@^7.12.10", "@babel/core@^7.12.3": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.9.tgz#bd96492c68822198f33e8a256061da3cf391f58f" - integrity sha512-G2EgeufBcYw27U4hhoIwFcgc1XU7TlXJ3mv04oOv1WCuo900U/anZSPzEqNjwdjgffkk2Gs0AN0dW1CKVLcG7w== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.22.10.tgz#aad442c7bcd1582252cb4576747ace35bc122f35" + integrity sha512-fTmqbbUBAwCcre6zPzNngvsI0aNrPZe77AeqvDxWM9Nm+04RrJ3CAmGHA9f7lJQY6ZMhRztNemy4uslDxTX4Qw== dependencies: "@ampproject/remapping" "^2.2.0" - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.9" - "@babel/helper-compilation-targets" "^7.22.9" + "@babel/code-frame" "^7.22.10" + "@babel/generator" "^7.22.10" + "@babel/helper-compilation-targets" "^7.22.10" "@babel/helper-module-transforms" "^7.22.9" - "@babel/helpers" "^7.22.6" - "@babel/parser" "^7.22.7" + "@babel/helpers" "^7.22.10" + "@babel/parser" "^7.22.10" "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.8" - "@babel/types" "^7.22.5" + "@babel/traverse" "^7.22.10" + "@babel/types" "^7.22.10" convert-source-map "^1.7.0" debug "^4.1.0" gensync "^1.0.0-beta.2" @@ -90,27 +91,27 @@ semver "^6.3.1" "@babel/eslint-parser@^7.12.10": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.9.tgz#75f8aa978d1e76c87cc6f26c1ea16ae58804d390" - integrity sha512-xdMkt39/nviO/4vpVdrEYPwXCsYIXSSAr6mC7WQsNIlGnuxKyKE7GZjalcnbSWiC4OXGNNN3UQPeHfjSC6sTDA== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/eslint-parser/-/eslint-parser-7.22.10.tgz#bfdf3d1b32ad573fe7c1c3447e0b485e3a41fd09" + integrity sha512-0J8DNPRXQRLeR9rPaUMM3fA+RbixjnVLe/MRMYCkp3hzgsSuxCHQ8NN8xQG1wIHKJ4a1DTROTvFJdW+B5/eOsg== dependencies: "@nicolo-ribaudo/eslint-scope-5-internals" "5.1.1-v1" eslint-visitor-keys "^2.1.0" semver "^6.3.1" "@babel/eslint-plugin@^7.12.10": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.22.5.tgz#47407d8c9e527b62ff75ee11e4baa6de3da7cf0e" - integrity sha512-lDXW06rf1sXywWWw+UdS/iYxRjrqhH4AXdPeKE4+fEgEoGBXcdIDQ+uCJOUcvCb0jCTvfwHOSXkwnfd24EAkLQ== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/eslint-plugin/-/eslint-plugin-7.22.10.tgz#b84e0f029b8f78604c3f6797cd6208cad46fbcf5" + integrity sha512-SRZcvo3fnO5h79B9DZSV6LG2vHH7OWsSNp1huFLHsXKyytRG413byQk9zxW1VcPOhnzfx2VIUz+8aGbiE7fOkA== dependencies: eslint-rule-composer "^0.3.0" -"@babel/generator@^7.22.7", "@babel/generator@^7.22.9", "@babel/generator@^7.7.2": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.9.tgz#572ecfa7a31002fa1de2a9d91621fd895da8493d" - integrity sha512-KtLMbmicyuK2Ak/FTCJVbDnkN1SlT8/kceFTiuDiiRUUSMnHMidxSCdG4ndkTOHHpoomWe/4xkvHkEOncwjYIw== +"@babel/generator@^7.22.10", "@babel/generator@^7.7.2": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.22.10.tgz#c92254361f398e160645ac58831069707382b722" + integrity sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.10" "@jridgewell/gen-mapping" "^0.3.2" "@jridgewell/trace-mapping" "^0.3.17" jsesc "^2.5.1" @@ -123,16 +124,16 @@ "@babel/types" "^7.22.5" "@babel/helper-builder-binary-assignment-operator-visitor@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.5.tgz#a3f4758efdd0190d8927fcffd261755937c71878" - integrity sha512-m1EP3lVOPptR+2DwD125gziZNcmoNSHGmJROKoy87loWUQyJaVXDgpmruWqDARZSmtYQ+Dl25okU8+qhVzuykw== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helper-builder-binary-assignment-operator-visitor/-/helper-builder-binary-assignment-operator-visitor-7.22.10.tgz#573e735937e99ea75ea30788b57eb52fab7468c9" + integrity sha512-Av0qubwDQxC56DoUReVDeLfMEjYYSN1nZrTUrWkXd7hpU73ymRANkbuDm3yni9npkn+RXy9nNbEJZEzXr7xrfQ== dependencies: - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.10" -"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6", "@babel/helper-compilation-targets@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.9.tgz#f9d0a7aaaa7cd32a3f31c9316a69f5a9bcacb892" - integrity sha512-7qYrNM6HjpnPHJbopxmb8hSPoZ0gsX8IvUS32JGVoy+pU9e5N0nLr1VjJoR6kA4d9dmGLxNYOjeB8sUDal2WMw== +"@babel/helper-compilation-targets@^7.20.7", "@babel/helper-compilation-targets@^7.22.10", "@babel/helper-compilation-targets@^7.22.5", "@babel/helper-compilation-targets@^7.22.6": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz#01d648bbc25dd88f513d862ee0df27b7d4e67024" + integrity sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q== dependencies: "@babel/compat-data" "^7.22.9" "@babel/helper-validator-option" "^7.22.5" @@ -140,10 +141,10 @@ lru-cache "^5.1.1" semver "^6.3.1" -"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.5", "@babel/helper-create-class-features-plugin@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.9.tgz#c36ea240bb3348f942f08b0fbe28d6d979fab236" - integrity sha512-Pwyi89uO4YrGKxL/eNJ8lfEH55DnRloGPOseaA8NFNL6jAUnn+KccaISiFazCj5IolPPDjGSdzQzXVzODVRqUQ== +"@babel/helper-create-class-features-plugin@^7.18.6", "@babel/helper-create-class-features-plugin@^7.22.10", "@babel/helper-create-class-features-plugin@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.10.tgz#dd2612d59eac45588021ac3d6fa976d08f4e95a3" + integrity sha512-5IBb77txKYQPpOEdUdIhBx8VrZyDCQ+H82H0+5dX1TmuscP5vJKEE3cKurjtIw/vFwzbVH48VweE78kVDBrqjA== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" "@babel/helper-environment-visitor" "^7.22.5" @@ -232,7 +233,7 @@ resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz#dd7ee3735e8a313b9f7b05a773d892e88e6d7295" integrity sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg== -"@babel/helper-remap-async-to-generator@^7.22.5": +"@babel/helper-remap-async-to-generator@^7.22.5", "@babel/helper-remap-async-to-generator@^7.22.9": version "7.22.9" resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.22.9.tgz#53a25b7484e722d7efb9c350c75c032d4628de82" integrity sha512-8WWC4oR4Px+tr+Fp0X3RHDVfINGpF3ad1HIbrc8A77epiR6eMMc6jsgozkzT2uDiOOdoS9cLIQ+XD2XvI2WSmQ== @@ -287,36 +288,36 @@ integrity sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw== "@babel/helper-wrap-function@^7.22.9": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.9.tgz#189937248c45b0182c1dcf32f3444ca153944cb9" - integrity sha512-sZ+QzfauuUEfxSEjKFmi3qDSHgLsTPK/pEpoD/qonZKOtTPTLbf59oabPQ4rKekt9lFcj/hTZaOhWwFYrgjk+Q== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.22.10.tgz#d845e043880ed0b8c18bd194a12005cb16d2f614" + integrity sha512-OnMhjWjuGYtdoO3FmsEFWvBStBAe2QOgwOLsLNDjN+aaiMD8InJk1/O3HSD8lkqTjCgg5YI34Tz15KNNA3p+nQ== dependencies: "@babel/helper-function-name" "^7.22.5" "@babel/template" "^7.22.5" - "@babel/types" "^7.22.5" + "@babel/types" "^7.22.10" -"@babel/helpers@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.6.tgz#8e61d3395a4f0c5a8060f309fb008200969b5ecd" - integrity sha512-YjDs6y/fVOYFV8hAf1rxd1QvR9wJe1pDBZ2AREKq/SDayfPzgk0PBnVuTCE5X1acEpMMNOVUqoe+OwiZGJ+OaA== +"@babel/helpers@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.22.10.tgz#ae6005c539dfbcb5cd71fb51bfc8a52ba63bc37a" + integrity sha512-a41J4NW8HyZa1I1vAndrraTlPZ/eZoga2ZgS7fEr0tZJGVU4xqdE80CEm0CcNjha5EZ8fTBYLKHF0kqDUuAwQw== dependencies: "@babel/template" "^7.22.5" - "@babel/traverse" "^7.22.6" - "@babel/types" "^7.22.5" + "@babel/traverse" "^7.22.10" + "@babel/types" "^7.22.10" -"@babel/highlight@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.5.tgz#aa6c05c5407a67ebce408162b7ede789b4d22031" - integrity sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw== +"@babel/highlight@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.22.10.tgz#02a3f6d8c1cb4521b2fd0ab0da8f4739936137d7" + integrity sha512-78aUtVcT7MUscr0K5mIEnkwxPE0MaxkR5RxRwuHaQ+JuU5AmTPhY+do2mdzVTnIJJpyBglql2pehuBIWHug+WQ== dependencies: "@babel/helper-validator-identifier" "^7.22.5" - chalk "^2.0.0" + chalk "^2.4.2" js-tokens "^4.0.0" -"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.5", "@babel/parser@^7.22.7": - version "7.22.7" - resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.7.tgz#df8cf085ce92ddbdbf668a7f186ce848c9036cae" - integrity sha512-7NF8pOkHP5o2vpmGgNGcfAeCvOYhGLyA3Z4eBQkT1RJlWu47n63bCs93QfJ2hIAFCil7L5P2IWhs1oToVgrL0Q== +"@babel/parser@^7.1.0", "@babel/parser@^7.14.7", "@babel/parser@^7.20.7", "@babel/parser@^7.22.10", "@babel/parser@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.22.10.tgz#e37634f9a12a1716136c44624ef54283cabd3f55" + integrity sha512-lNbdGsQb9ekfsnjFGhEiF4hfFqGgfOP3H3d27re3n+CGhNuTSUEQdfWk556sTLNTloczcdM5TYF2LhzmDQKyvQ== "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.22.5": version "7.22.5" @@ -366,14 +367,6 @@ resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-private-property-in-object/-/plugin-proposal-private-property-in-object-7.21.0-placeholder-for-preset-env.2.tgz#7844f9289546efa9febac2de4cfe358a050bd703" integrity sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w== -"@babel/plugin-proposal-unicode-property-regex@^7.4.4": - version "7.18.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.18.6.tgz#af613d2cd5e643643b65cded64207b15c85cb78e" - integrity sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w== - dependencies: - "@babel/helper-create-regexp-features-plugin" "^7.18.6" - "@babel/helper-plugin-utils" "^7.18.6" - "@babel/plugin-syntax-async-generators@^7.8.4": version "7.8.4" resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d" @@ -529,14 +522,14 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-async-generator-functions@^7.22.7": - version "7.22.7" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.7.tgz#053e76c0a903b72b573cb1ab7d6882174d460a1b" - integrity sha512-7HmE7pk/Fmke45TODvxvkxRMV9RazV+ZZzhOL9AG8G29TLrr3jkjwF7uJfxZ30EoXpO+LJkq4oA8NjO2DTnEDg== +"@babel/plugin-transform-async-generator-functions@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-generator-functions/-/plugin-transform-async-generator-functions-7.22.10.tgz#45946cd17f915b10e65c29b8ed18a0a50fc648c8" + integrity sha512-eueE8lvKVzq5wIObKK/7dvoeKJ+xc6TvRn6aysIjS6pSCeLy7S/eVi7pEQknZqyqvzaNKdDtem8nUNTBgDVR2g== dependencies: "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" - "@babel/helper-remap-async-to-generator" "^7.22.5" + "@babel/helper-remap-async-to-generator" "^7.22.9" "@babel/plugin-syntax-async-generators" "^7.8.4" "@babel/plugin-transform-async-to-generator@^7.22.5": @@ -555,10 +548,10 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-block-scoping@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.5.tgz#8bfc793b3a4b2742c0983fadc1480d843ecea31b" - integrity sha512-EcACl1i5fSQ6bt+YGuU/XGCeZKStLmyVGytWkpyhCLeQVA0eu6Wtiw92V+I1T/hnezUv7j74dA/Ro69gWcU+hg== +"@babel/plugin-transform-block-scoping@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-block-scoping/-/plugin-transform-block-scoping-7.22.10.tgz#88a1dccc3383899eb5e660534a76a22ecee64faa" + integrity sha512-1+kVpGAOOI1Albt6Vse7c8pHzcZQdQKW+wJH+g8mCaszOdDVwRXa/slHPqIw+oJAJANTKDMuM2cBdV0Dg618Vg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -602,14 +595,14 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/template" "^7.22.5" -"@babel/plugin-transform-destructuring@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.5.tgz#d3aca7438f6c26c78cdd0b0ba920a336001b27cc" - integrity sha512-GfqcFuGW8vnEqTUBM7UtPd5A4q797LTvvwKxXTgRsFjoqaJiEg9deBG6kWeQYkVEL569NpnmpC0Pkr/8BLKGnQ== +"@babel/plugin-transform-destructuring@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.22.10.tgz#38e2273814a58c810b6c34ea293be4973c4eb5e2" + integrity sha512-dPJrL0VOyxqLM9sritNbMSGx/teueHF/htMKrPT7DNxccXxRDPYqlgPFFdr8u+F+qUZOkZoXue/6rL5O5GduEw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-dotall-regex@^7.22.5", "@babel/plugin-transform-dotall-regex@^7.4.4": +"@babel/plugin-transform-dotall-regex@^7.22.5": version "7.22.5" resolved "https://registry.yarnpkg.com/@babel/plugin-transform-dotall-regex/-/plugin-transform-dotall-regex-7.22.5.tgz#dbb4f0e45766eb544e193fb00e65a1dd3b2a4165" integrity sha512-5/Yk9QxCQCl+sOIB1WelKnVRxTJDSAIxtJLL2/pqL14ZVlbH0fUQUZa/T5/UnQtBNgghR7mfB8ERBKyKPCi7Vw== @@ -787,10 +780,10 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-optional-catch-binding" "^7.8.3" -"@babel/plugin-transform-optional-chaining@^7.22.5", "@babel/plugin-transform-optional-chaining@^7.22.6": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.6.tgz#4bacfe37001fe1901117672875e931d439811564" - integrity sha512-Vd5HiWml0mDVtcLHIoEU5sw6HOUW/Zk0acLs/SAeuLzkGNOPc9DB4nkUajemhCmTIz3eiaKREZn2hQQqF79YTg== +"@babel/plugin-transform-optional-chaining@^7.22.10", "@babel/plugin-transform-optional-chaining@^7.22.5": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.22.10.tgz#076d28a7e074392e840d4ae587d83445bac0372a" + integrity sha512-MMkQqZAZ+MGj+jGTG3OTuhKeBpNcO+0oCEbrGNEaOmiEn+1MzRyQlYsruGiU8RTK3zV6XwrVJTmwiDOyYK6J9g== dependencies: "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-skip-transparent-expression-wrappers" "^7.22.5" @@ -828,13 +821,13 @@ dependencies: "@babel/helper-plugin-utils" "^7.22.5" -"@babel/plugin-transform-regenerator@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.5.tgz#cd8a68b228a5f75fa01420e8cc2fc400f0fc32aa" - integrity sha512-rR7KePOE7gfEtNTh9Qw+iO3Q/e4DEsoQ+hdvM6QUDH7JRJ5qxq5AA52ZzBWbI5i9lfNuvySgOGP8ZN7LAmaiPw== +"@babel/plugin-transform-regenerator@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.22.10.tgz#8ceef3bd7375c4db7652878b0241b2be5d0c3cca" + integrity sha512-F28b1mDt8KcT5bUyJc/U9nwzw6cV+UmTeRlXYIl2TNqMMJif0Jeey9/RQ3C4NOd2zp0/TRsDns9ttj2L523rsw== dependencies: "@babel/helper-plugin-utils" "^7.22.5" - regenerator-transform "^0.15.1" + regenerator-transform "^0.15.2" "@babel/plugin-transform-reserved-words@^7.22.5": version "7.22.5" @@ -844,15 +837,15 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-runtime@^7.12.10": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.9.tgz#a87b11e170cbbfb018e6a2bf91f5c6e533b9e027" - integrity sha512-9KjBH61AGJetCPYp/IEyLEp47SyybZb0nDRpBvmtEkm+rUIwxdlKpyNHI1TmsGkeuLclJdleQHRZ8XLBnnh8CQ== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.22.10.tgz#89eda6daf1d3af6f36fb368766553054c8d7cd46" + integrity sha512-RchI7HePu1eu0CYNKHHHQdfenZcM4nz8rew5B1VWqeRKdcwW5aQ5HeG9eTUbWiAS1UrmHVLmoxTWHt3iLD/NhA== dependencies: "@babel/helper-module-imports" "^7.22.5" "@babel/helper-plugin-utils" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.4" - babel-plugin-polyfill-corejs3 "^0.8.2" - babel-plugin-polyfill-regenerator "^0.5.1" + babel-plugin-polyfill-corejs2 "^0.4.5" + babel-plugin-polyfill-corejs3 "^0.8.3" + babel-plugin-polyfill-regenerator "^0.5.2" semver "^6.3.1" "@babel/plugin-transform-shorthand-properties@^7.22.5": @@ -892,19 +885,19 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-transform-typescript@^7.22.5": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.9.tgz#91e08ad1eb1028ecc62662a842e93ecfbf3c7234" - integrity sha512-BnVR1CpKiuD0iobHPaM1iLvcwPYN2uVFAqoLVSpEDKWuOikoCv5HbKLxclhKYUXlWkX86DoZGtqI4XhbOsyrMg== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.10.tgz#aadd98fab871f0bb5717bcc24c31aaaa455af923" + integrity sha512-7++c8I/ymsDo4QQBAgbraXLzIM6jmfao11KgIBEYZRReWzNWH9NtNgJcyrZiXsOPh523FQm6LfpLyy/U5fn46A== dependencies: "@babel/helper-annotate-as-pure" "^7.22.5" - "@babel/helper-create-class-features-plugin" "^7.22.9" + "@babel/helper-create-class-features-plugin" "^7.22.10" "@babel/helper-plugin-utils" "^7.22.5" "@babel/plugin-syntax-typescript" "^7.22.5" -"@babel/plugin-transform-unicode-escapes@^7.22.5": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.5.tgz#ce0c248522b1cb22c7c992d88301a5ead70e806c" - integrity sha512-biEmVg1IYB/raUO5wT1tgfacCef15Fbzhkx493D3urBI++6hpJ+RFG4SrWMn0NEZLfvilqKf3QDrRVZHo08FYg== +"@babel/plugin-transform-unicode-escapes@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-escapes/-/plugin-transform-unicode-escapes-7.22.10.tgz#c723f380f40a2b2f57a62df24c9005834c8616d9" + integrity sha512-lRfaRKGZCBqDlRU3UIFovdp9c9mEvlylmpod0/OatICsSfuQ9YFthRo1tpTkGsklEefZdqlEFdY4A2dwTb6ohg== dependencies: "@babel/helper-plugin-utils" "^7.22.5" @@ -933,12 +926,12 @@ "@babel/helper-plugin-utils" "^7.22.5" "@babel/preset-env@^7.12.11": - version "7.22.9" - resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.9.tgz#57f17108eb5dfd4c5c25a44c1977eba1df310ac7" - integrity sha512-wNi5H/Emkhll/bqPjsjQorSykrlfY5OWakd6AulLvMEytpKasMVUpVy8RL4qBIBs5Ac6/5i0/Rv0b/Fg6Eag/g== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.22.10.tgz#3263b9fe2c8823d191d28e61eac60a79f9ce8a0f" + integrity sha512-riHpLb1drNkpLlocmSyEg4oYJIQFeXAK/d7rI6mbD0XsvoTOOweXDmQPG/ErxsEhWk3rl3Q/3F6RFQlVFS8m0A== dependencies: "@babel/compat-data" "^7.22.9" - "@babel/helper-compilation-targets" "^7.22.9" + "@babel/helper-compilation-targets" "^7.22.10" "@babel/helper-plugin-utils" "^7.22.5" "@babel/helper-validator-option" "^7.22.5" "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.22.5" @@ -963,15 +956,15 @@ "@babel/plugin-syntax-top-level-await" "^7.14.5" "@babel/plugin-syntax-unicode-sets-regex" "^7.18.6" "@babel/plugin-transform-arrow-functions" "^7.22.5" - "@babel/plugin-transform-async-generator-functions" "^7.22.7" + "@babel/plugin-transform-async-generator-functions" "^7.22.10" "@babel/plugin-transform-async-to-generator" "^7.22.5" "@babel/plugin-transform-block-scoped-functions" "^7.22.5" - "@babel/plugin-transform-block-scoping" "^7.22.5" + "@babel/plugin-transform-block-scoping" "^7.22.10" "@babel/plugin-transform-class-properties" "^7.22.5" "@babel/plugin-transform-class-static-block" "^7.22.5" "@babel/plugin-transform-classes" "^7.22.6" "@babel/plugin-transform-computed-properties" "^7.22.5" - "@babel/plugin-transform-destructuring" "^7.22.5" + "@babel/plugin-transform-destructuring" "^7.22.10" "@babel/plugin-transform-dotall-regex" "^7.22.5" "@babel/plugin-transform-duplicate-keys" "^7.22.5" "@babel/plugin-transform-dynamic-import" "^7.22.5" @@ -994,38 +987,36 @@ "@babel/plugin-transform-object-rest-spread" "^7.22.5" "@babel/plugin-transform-object-super" "^7.22.5" "@babel/plugin-transform-optional-catch-binding" "^7.22.5" - "@babel/plugin-transform-optional-chaining" "^7.22.6" + "@babel/plugin-transform-optional-chaining" "^7.22.10" "@babel/plugin-transform-parameters" "^7.22.5" "@babel/plugin-transform-private-methods" "^7.22.5" "@babel/plugin-transform-private-property-in-object" "^7.22.5" "@babel/plugin-transform-property-literals" "^7.22.5" - "@babel/plugin-transform-regenerator" "^7.22.5" + "@babel/plugin-transform-regenerator" "^7.22.10" "@babel/plugin-transform-reserved-words" "^7.22.5" "@babel/plugin-transform-shorthand-properties" "^7.22.5" "@babel/plugin-transform-spread" "^7.22.5" "@babel/plugin-transform-sticky-regex" "^7.22.5" "@babel/plugin-transform-template-literals" "^7.22.5" "@babel/plugin-transform-typeof-symbol" "^7.22.5" - "@babel/plugin-transform-unicode-escapes" "^7.22.5" + "@babel/plugin-transform-unicode-escapes" "^7.22.10" "@babel/plugin-transform-unicode-property-regex" "^7.22.5" "@babel/plugin-transform-unicode-regex" "^7.22.5" "@babel/plugin-transform-unicode-sets-regex" "^7.22.5" - "@babel/preset-modules" "^0.1.5" - "@babel/types" "^7.22.5" - babel-plugin-polyfill-corejs2 "^0.4.4" - babel-plugin-polyfill-corejs3 "^0.8.2" - babel-plugin-polyfill-regenerator "^0.5.1" + "@babel/preset-modules" "0.1.6-no-external-plugins" + "@babel/types" "^7.22.10" + babel-plugin-polyfill-corejs2 "^0.4.5" + babel-plugin-polyfill-corejs3 "^0.8.3" + babel-plugin-polyfill-regenerator "^0.5.2" core-js-compat "^3.31.0" semver "^6.3.1" -"@babel/preset-modules@^0.1.5": - version "0.1.6" - resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6.tgz#31bcdd8f19538437339d17af00d177d854d9d458" - integrity sha512-ID2yj6K/4lKfhuU3+EX4UvNbIt7eACFbHmNUjzA+ep+B5971CknnA/9DEWKbRokfbbtblxxxXFJJrH47UEAMVg== +"@babel/preset-modules@0.1.6-no-external-plugins": + version "0.1.6-no-external-plugins" + resolved "https://registry.yarnpkg.com/@babel/preset-modules/-/preset-modules-0.1.6-no-external-plugins.tgz#ccb88a2c49c817236861fee7826080573b8a923a" + integrity sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA== dependencies: "@babel/helper-plugin-utils" "^7.0.0" - "@babel/plugin-proposal-unicode-property-regex" "^7.4.4" - "@babel/plugin-transform-dotall-regex" "^7.4.4" "@babel/types" "^7.4.4" esutils "^2.0.2" @@ -1057,11 +1048,11 @@ integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== "@babel/runtime@^7.0.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.8.4": - version "7.22.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.6.tgz#57d64b9ae3cff1d67eb067ae117dac087f5bd438" - integrity sha512-wDb5pWm4WDdF6LFUde3Jl8WzPA+3ZbxYqkC6xAXuD3irdEHN1k0NfTRrJD8ZD378SJ61miMLCqIOXYhd8x+AJQ== + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.10.tgz#ae3e9631fd947cb7e3610d3e9d8fef5f76696682" + integrity sha512-21t/fkKLMZI4pqP2wlmsQAWnYW1PDyKyyUV4vCi+B25ydmdaYTKXPwCj0BzSUnZf4seIiYvSA3jcZ3gdsMFkLQ== dependencies: - regenerator-runtime "^0.13.11" + regenerator-runtime "^0.14.0" "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.5" @@ -1072,26 +1063,26 @@ "@babel/parser" "^7.22.5" "@babel/types" "^7.22.5" -"@babel/traverse@^7.22.6", "@babel/traverse@^7.22.8": - version "7.22.8" - resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.8.tgz#4d4451d31bc34efeae01eac222b514a77aa4000e" - integrity sha512-y6LPR+wpM2I3qJrsheCTwhIinzkETbplIgPBbwvqPKc+uljeA5gP+3nP8irdYt1mjQaDnlIcG+dw8OjAco4GXw== +"@babel/traverse@^7.22.10": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.22.10.tgz#20252acb240e746d27c2e82b4484f199cf8141aa" + integrity sha512-Q/urqV4pRByiNNpb/f5OSv28ZlGJiFiiTh+GAHktbIrkPhPbl90+uW6SmpoLyZqutrg9AEaEf3Q/ZBRHBXgxig== dependencies: - "@babel/code-frame" "^7.22.5" - "@babel/generator" "^7.22.7" + "@babel/code-frame" "^7.22.10" + "@babel/generator" "^7.22.10" "@babel/helper-environment-visitor" "^7.22.5" "@babel/helper-function-name" "^7.22.5" "@babel/helper-hoist-variables" "^7.22.5" "@babel/helper-split-export-declaration" "^7.22.6" - "@babel/parser" "^7.22.7" - "@babel/types" "^7.22.5" + "@babel/parser" "^7.22.10" + "@babel/types" "^7.22.10" debug "^4.1.0" globals "^11.1.0" -"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": - version "7.22.5" - resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.5.tgz#cd93eeaab025880a3a47ec881f4b096a5b786fbe" - integrity sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA== +"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.22.10", "@babel/types@^7.22.5", "@babel/types@^7.3.3", "@babel/types@^7.4.4": + version "7.22.10" + resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.22.10.tgz#4a9e76446048f2c66982d1a989dd12b8a2d2dc03" + integrity sha512-obaoigiLrlDZ7TUQln/8m4mSqIW2QFeOrCQc9r+xsaHGNoplVNYlRVpsfE8Vj35GEm2ZH4ZhrNYogs/3fj85kg== dependencies: "@babel/helper-string-parser" "^7.22.5" "@babel/helper-validator-identifier" "^7.22.5" @@ -1118,12 +1109,12 @@ dependencies: "@jridgewell/trace-mapping" "0.3.9" -"@es-joy/jsdoccomment@~0.39.4": - version "0.39.4" - resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.39.4.tgz#6b8a62e9b3077027837728818d3c4389a898b392" - integrity sha512-Jvw915fjqQct445+yron7Dufix9A+m9j1fCJYlCo1FWlRvTxa3pjJelxdSTdaLWcTwRU6vbL+NYjO4YuNIS5Qg== +"@es-joy/jsdoccomment@~0.40.1": + version "0.40.1" + resolved "https://registry.yarnpkg.com/@es-joy/jsdoccomment/-/jsdoccomment-0.40.1.tgz#13acd77fb372ed1c83b7355edd865a3b370c9ec4" + integrity sha512-YORCdZSusAlBrFpZ77pJjc5r1bQs5caPWtAu+WWmiSo+8XaUzseapVrfAtiRFbQWnrBxxLLEwF6f6ZG/UgCQCg== dependencies: - comment-parser "1.3.1" + comment-parser "1.4.0" esquery "^1.5.0" jsdoc-type-pratt-parser "~4.0.0" @@ -1134,15 +1125,15 @@ dependencies: eslint-visitor-keys "^3.3.0" -"@eslint-community/regexpp@^4.4.0": +"@eslint-community/regexpp@^4.4.0", "@eslint-community/regexpp@^4.6.1": version "4.6.2" resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.6.2.tgz#1816b5f6948029c5eaacb0703b850ee0cb37d8f8" integrity sha512-pPTNuaAG3QMH+buKyBIGJs3g/S5y0caxw0ygM3YyE6yJFySwiGGSzA+mM3KJ8QQvzeLh3blwgSonkFjgQdxzMw== -"@eslint/eslintrc@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.0.tgz#82256f164cc9e0b59669efc19d57f8092706841d" - integrity sha512-Lj7DECXqIVCqnqjjHMPna4vn6GJcMgul/wuS0je9OZ9gsL0zzDpKPVtcG1HaDVc+9y+qgXneTeUMbCqXJNpH1A== +"@eslint/eslintrc@^2.1.1": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-2.1.1.tgz#18d635e24ad35f7276e8a49d135c7d3ca6a46f93" + integrity sha512-9t7ZA7NGGK8ckelF0PQCfcxIUzs1Md5rrO6U/c+FIQNanea5UZC0wqKXH4vHBccmu4ZJgZ2idtPeW7+Q2npOEA== dependencies: ajv "^6.12.4" debug "^4.3.2" @@ -1154,10 +1145,10 @@ minimatch "^3.1.2" strip-json-comments "^3.1.1" -"@eslint/js@8.44.0": - version "8.44.0" - resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.44.0.tgz#961a5903c74139390478bdc808bcde3fc45ab7af" - integrity sha512-Ag+9YM4ocKQx9AarydN0KY2j0ErMHNIocPDrVo8zAE44xLTjEtz81OdR68/cydGtk6m6jDb5Za3r2useMzYmSw== +"@eslint/js@^8.46.0": + version "8.46.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.46.0.tgz#3f7802972e8b6fe3f88ed1aabc74ec596c456db6" + integrity sha512-a8TLtmPi8xzPkCbp/OGFUo5yhRkHM2Ko9kOWP4znJr0WAhWyThaw3PnwX4vOTWOAMsV2uRt32PPDcEz63esSaA== "@humanwhocodes/config-array@^0.11.10": version "0.11.10" @@ -1433,12 +1424,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/resolve-uri@3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz#2203b118c157721addfe69d47b70465463066d78" - integrity sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w== - -"@jridgewell/resolve-uri@^3.0.3": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.1" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== @@ -1456,12 +1442,7 @@ "@jridgewell/gen-mapping" "^0.3.0" "@jridgewell/trace-mapping" "^0.3.9" -"@jridgewell/sourcemap-codec@1.4.14": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz#add4c98d341472a289190b424efbdb096991bb24" - integrity sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw== - -"@jridgewell/sourcemap-codec@^1.4.10": +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": version "1.4.15" resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== @@ -1475,17 +1456,17 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping@^0.3.12", "@jridgewell/trace-mapping@^0.3.17", "@jridgewell/trace-mapping@^0.3.18", "@jridgewell/trace-mapping@^0.3.9": - version "0.3.18" - resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz#25783b2086daf6ff1dcb53c9249ae480e4dd4cd6" - integrity sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA== + version "0.3.19" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811" + integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw== dependencies: - "@jridgewell/resolve-uri" "3.1.0" - "@jridgewell/sourcemap-codec" "1.4.14" + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" "@matrix-org/matrix-sdk-crypto-wasm@^1.2.0": - version "1.2.0" - resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.2.0.tgz#115cd21cb2bba3c8166cf09e7d61da0902aa8973" - integrity sha512-vmpbtXYFzfBSFjeAx/PNRjy7zyH+Xx2HVXNKdApgo3+hSALewcXwdOTJy5pKq+poumM2TjjKDhG2s6/zSDNUYg== + version "1.2.1" + resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.2.1.tgz#5b546c8a0e53b614f10b77b3b649818aed9d0db1" + integrity sha512-DCb7Q83PCQK0uav5vB3KNV/hJPlxAhT/ddar+VHz2kC39hMLKGzWYVhprpLYVcavaE/6OX+Q/xFkAoV/3QtUHQ== "@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz": version "3.2.14" @@ -1830,14 +1811,14 @@ integrity sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== "@types/node@*": - version "20.4.5" - resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.5.tgz#9dc0a5cb1ccce4f7a731660935ab70b9c00a5d69" - integrity sha512-rt40Nk13II9JwQBdeYqmbn2Q6IVTA5uPhvSO+JVqdXw/6/4glI6oR9ezty/A9Hg5u7JH4OmYmuQ+XvjKm0Datg== + version "20.4.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.4.8.tgz#b5dda19adaa473a9bf0ab5cbd8f30ec7d43f5c85" + integrity sha512-0mHckf6D2DiIAzh8fM8f3HQCvMKDpK94YQ0DSVkfWTG9BZleYIWudw9cJxX8oCk9bM+vAkDyujDV6dmKHbvQpg== "@types/node@18": - version "18.17.1" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.1.tgz#84c32903bf3a09f7878c391d31ff08f6fe7d8335" - integrity sha512-xlR1jahfizdplZYRU59JlUx9uzF1ARa8jbhM11ccpCJya8kvos5jwdm2ZAgxSCwOl0fq21svP18EVwPBXMQudw== + version "18.17.3" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.17.3.tgz#409febdc84478b452306a8112c692e800ad9f6fe" + integrity sha512-2x8HWtFk0S99zqVQABU9wTpr8wPoaDHZUcAkoTKH+nL7kPv3WUI9cRi/Kk5Mz4xdqXSqTkKP7IWNoQQYCnDsTA== "@types/normalize-package-data@^2.4.0": version "2.4.1" @@ -2037,7 +2018,7 @@ agent-base@6: dependencies: debug "4" -ajv@^6.10.0, ajv@^6.12.4, ajv@~6.12.6: +ajv@^6.12.4, ajv@~6.12.6: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2286,7 +2267,7 @@ babel-plugin-jest-hoist@^29.5.0: "@types/babel__core" "^7.1.14" "@types/babel__traverse" "^7.0.6" -babel-plugin-polyfill-corejs2@^0.4.4: +babel-plugin-polyfill-corejs2@^0.4.5: version "0.4.5" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs2/-/babel-plugin-polyfill-corejs2-0.4.5.tgz#8097b4cb4af5b64a1d11332b6fb72ef5e64a054c" integrity sha512-19hwUH5FKl49JEsvyTcoHakh6BE0wgXLLptIyKZ3PijHc/Ci521wygORCUCCred+E/twuqRyAkE02BAWPmsHOg== @@ -2295,7 +2276,7 @@ babel-plugin-polyfill-corejs2@^0.4.4: "@babel/helper-define-polyfill-provider" "^0.4.2" semver "^6.3.1" -babel-plugin-polyfill-corejs3@^0.8.2: +babel-plugin-polyfill-corejs3@^0.8.3: version "0.8.3" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.8.3.tgz#b4f719d0ad9bb8e0c23e3e630c0c8ec6dd7a1c52" integrity sha512-z41XaniZL26WLrvjy7soabMXrfPWARN25PZoriDEiLMxAp50AUW3t35BGQUMg5xK3UrpVTtagIDklxYa+MhiNA== @@ -2303,7 +2284,7 @@ babel-plugin-polyfill-corejs3@^0.8.2: "@babel/helper-define-polyfill-provider" "^0.4.2" core-js-compat "^3.31.0" -babel-plugin-polyfill-regenerator@^0.5.1: +babel-plugin-polyfill-regenerator@^0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-regenerator/-/babel-plugin-polyfill-regenerator-0.5.2.tgz#80d0f3e1098c080c8b5a65f41e9427af692dc326" integrity sha512-tAlOptU0Xj34V1Y2PNTL4Y0FOJMDB6bZmoW39FeCQIhigGLkqu3Fj6uiXpxIf6Ij274ENdYx64y6Au+ZKlb1IA== @@ -2572,13 +2553,13 @@ browserify@^17.0.0: xtend "^4.0.0" browserslist@^4.21.9: - version "4.21.9" - resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.9.tgz#e11bdd3c313d7e2a9e87e8b4b0c7872b13897635" - integrity sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg== + version "4.21.10" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.21.10.tgz#dbbac576628c13d3b2231332cb2ec5a46e015bb0" + integrity sha512-bipEBdZfVH5/pwrvqc+Ub0kUPVfGUhlKxbvfD+z1BDnPEO/X98ruXGA1WP5ASpAFKan7Qr6j736IacbZQuAlKQ== dependencies: - caniuse-lite "^1.0.30001503" - electron-to-chromium "^1.4.431" - node-releases "^2.0.12" + caniuse-lite "^1.0.30001517" + electron-to-chromium "^1.4.477" + node-releases "^2.0.13" update-browserslist-db "^1.0.11" bs58@^5.0.0: @@ -2658,12 +2639,12 @@ camelcase@^6.2.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== -caniuse-lite@^1.0.30001503: - version "1.0.30001517" - resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001517.tgz#90fabae294215c3495807eb24fc809e11dc2f0a8" - integrity sha512-Vdhm5S11DaFVLlyiKu4hiUTkpZu+y1KA/rZZqVQfOD5YdDT/eQKlkt7NaE0WGOFgX32diqt9MiP9CAiFeRklaA== +caniuse-lite@^1.0.30001517: + version "1.0.30001519" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001519.tgz#3e7b8b8a7077e78b0eb054d69e6edf5c7df35601" + integrity sha512-0QHgqR+Jv4bxHMp8kZ1Kn8CH55OikjKJ6JmKkZYP1F3D7w+lnFXF70nG5eNfsZS89jadi5Ywy5UCSKLAglIRkg== -chalk@^2.0.0, chalk@^2.4.1: +chalk@^2.4.1, chalk@^2.4.2: version "2.4.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== @@ -2823,10 +2804,10 @@ commander@^4.0.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -comment-parser@1.3.1: - version "1.3.1" - resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.3.1.tgz#3d7ea3adaf9345594aedee6563f422348f165c1b" - integrity sha512-B52sN2VNghyq5ofvUsqZjmk6YkihBX5vMSChmSK9v4ShjKf3Vk5Xcmgpw4o+iIgtrnM/u5FiMpz9VKb8lpBveA== +comment-parser@1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/comment-parser/-/comment-parser-1.4.0.tgz#0f8c560f59698193854f12884c20c0e39a26d32c" + integrity sha512-QLyTNiZ2KDOibvFPlZ6ZngVsZ/0gYnE6uTXi5aoDg8ed3AkJAz4sEje3Y8a29hQ1s6A99MZXe47fLAXQ1rTqaw== commondir@^1.0.1: version "1.0.1" @@ -2879,16 +2860,16 @@ convert-source-map@~1.1.0: integrity sha512-Y8L5rp6jo+g9VEPgvqNfEopjTR4OTYct8lXlS8iVQdmnjDvbdbzYe9rjtFCB9egC86JoNCU61WRY+ScjkZpnIg== core-js-compat@^3.31.0: - version "3.31.1" - resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.31.1.tgz#5084ad1a46858df50ff89ace152441a63ba7aae0" - integrity sha512-wIDWd2s5/5aJSdpOJHfSibxNODxoGoWOBHt8JSPB41NOE94M7kuTPZCYLOlTtuoXTsBPKobpJ6T+y0SSy5L9SA== + version "3.32.0" + resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.32.0.tgz#f41574b6893ab15ddb0ac1693681bd56c8550a90" + integrity sha512-7a9a3D1k4UCVKnLhrgALyFcP7YCsLOQIxPd0dKjf/6GuPcgyiGP70ewWdCGrSK7evyhymi0qO4EqCmSJofDeYw== dependencies: browserslist "^4.21.9" core-js@^3.0.0: - version "3.31.1" - resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.31.1.tgz#f2b0eea9be9da0def2c5fece71064a7e5d687653" - integrity sha512-2sKLtfq1eFST7l7v62zaqXacPc7uG8ZAya8ogijLhTtaKNcpzpB4TMoTw2Si+8GYKRwFPMMtUT0263QFWFfqyQ== + version "3.32.0" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.32.0.tgz#7643d353d899747ab1f8b03d2803b0312a0fb3b6" + integrity sha512-rd4rYZNlF3WuoYuRIDEmbR/ga9CeuWX9U05umAvgrrZoHY4Z++cp/xwPQMvUpBB4Ag6J8KfD80G0zwCyaSxDww== core-util-is@~1.0.0: version "1.0.3" @@ -3021,9 +3002,9 @@ decimal.js@^10.4.2: integrity sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA== dedent@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.2.0.tgz#32039cd75c035f684e01c4a07cb88c0ecbeb57be" - integrity sha512-i4tcg0ClgvMUSxwHpt+NHQ01ZJmAkl6eBvDNrSZG9e+oLRTCSHv0wpr/Bzjpf6CwKeIHGevE1M34Y1Axdms5VQ== + version "1.5.1" + resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.5.1.tgz#4f3fc94c8b711e9bb2800d185cd6ad20f2a90aff" + integrity sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg== deep-is@^0.1.3: version "0.1.4" @@ -3194,10 +3175,10 @@ eastasianwidth@^0.2.0: resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== -electron-to-chromium@^1.4.431: - version "1.4.473" - resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.473.tgz#4853de13a335c70fe1f9df8d4029be54068767d1" - integrity sha512-aVfC8+440vGfl06l8HKKn8/PD5jRfSnLkTTD65EFvU46igbpQRri1gxSzW9/+TeUlwYzrXk1sw867T96zlyECA== +electron-to-chromium@^1.4.477: + version "1.4.487" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.487.tgz#e2ef8b15f2791bf68fa6f38f2656f1a551d360ae" + integrity sha512-XbCRs/34l31np/p33m+5tdBrdXu9jJkZxSbNxj5I0H1KtV2ZMSB+i/HYqDiRzHaFx2T5EdytjoBRe8QRJE2vQg== elliptic@^6.5.3: version "6.5.4" @@ -3389,19 +3370,19 @@ eslint-config-google@^0.14.0: resolved "https://registry.yarnpkg.com/eslint-config-google/-/eslint-config-google-0.14.0.tgz#4f5f8759ba6e11b424294a219dbfa18c508bcc1a" integrity sha512-WsbX4WbjuMvTdeVL6+J3rK1RGhCTqjsFjX7UMSMgZiyxxaNLkoJENbrGExzERFeoTpGw3F3FypTiWAP9ZXzkEw== -eslint-config-prettier@^8.5.0: - version "8.9.0" - resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-8.9.0.tgz#094b6254b2804b0544f7cee535f802b6d29ee10b" - integrity sha512-+sbni7NfVXnOpnRadUA8S28AUlsZt9GjgFvABIRL9Hkn8KqNzOp+7Lw4QWtrwn20KzU3wqu1QoOj2m+7rKRqkA== +eslint-config-prettier@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f" + integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw== eslint-import-resolver-node@^0.3.7: - version "0.3.7" - resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz#83b375187d412324a1963d84fa664377a23eb4d7" - integrity sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA== + version "0.3.9" + resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac" + integrity sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g== dependencies: debug "^3.2.7" - is-core-module "^2.11.0" - resolve "^1.22.1" + is-core-module "^2.13.0" + resolve "^1.22.4" eslint-import-resolver-typescript@^3.5.1: version "3.5.5" @@ -3456,13 +3437,13 @@ eslint-plugin-jest@^27.1.6: "@typescript-eslint/utils" "^5.10.0" eslint-plugin-jsdoc@^46.0.0: - version "46.4.5" - resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.5.tgz#f06fd71505d60078a8e4dff792f7cb20261e7e58" - integrity sha512-HjTuxqDYplAQFu29F3MHFCDDBgeqOxPXI6TyBhL0u2rr4XntJ0z3C9PmJvpjFscKdHwkZDN/0l1QCG0QwyRi4g== + version "46.4.6" + resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-46.4.6.tgz#5226461eda61b5920297cbe02c3b17bc9423cf0b" + integrity sha512-z4SWYnJfOqftZI+b3RM9AtWL1vF/sLWE/LlO9yOKDof9yN2+n3zOdOJTGX/pRE/xnPsooOLG2Rq6e4d+XW3lNw== dependencies: - "@es-joy/jsdoccomment" "~0.39.4" + "@es-joy/jsdoccomment" "~0.40.1" are-docs-informative "^0.0.2" - comment-parser "1.3.1" + comment-parser "1.4.0" debug "^4.3.4" escape-string-regexp "^4.0.0" esquery "^1.5.0" @@ -3471,9 +3452,9 @@ eslint-plugin-jsdoc@^46.0.0: spdx-expression-parse "^3.0.1" eslint-plugin-matrix-org@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-1.2.0.tgz#84b78969c93e6d3d593fe8bf25ee67ec4dcd2883" - integrity sha512-Wp5CeLnyEwGBn8ZfVbSuO2y0Fs51IWonPJ1QRQTntaRxOkEQnnky3gOPwpfGJ8JB0CxYr1zXfeHh8LcYHW4wcg== + version "1.2.1" + resolved "https://registry.yarnpkg.com/eslint-plugin-matrix-org/-/eslint-plugin-matrix-org-1.2.1.tgz#76d1505daa93fb99ba4156008b9b32f57682c9b1" + integrity sha512-A3cDjhG7RHwfCS8o3bOip8hSCsxtmgk2ahvqE5v/Ic2kPEZxixY6w8zLj7hFGsrRmPSEpLWqkVLt8uvQBapiQA== eslint-plugin-tsdoc@^0.2.17: version "0.2.17" @@ -3517,10 +3498,10 @@ eslint-scope@5.1.1, eslint-scope@^5.1.1: esrecurse "^4.3.0" estraverse "^4.1.1" -eslint-scope@^7.2.0: - version "7.2.1" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.1.tgz#936821d3462675f25a18ac5fd88a67cc15b393bd" - integrity sha512-CvefSOsDdaYYvxChovdrPo/ZGt8d5lrJWleAc1diXRKhHGiTYEI26cvo8Kle/wGnsizoCJjK73FMg1/IkIwiNA== +eslint-scope@^7.2.2: + version "7.2.2" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.2.2.tgz#deb4f92563390f32006894af62a22dba1c46423f" + integrity sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg== dependencies: esrecurse "^4.3.0" estraverse "^5.2.0" @@ -3530,32 +3511,32 @@ eslint-visitor-keys@^2.1.0: resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303" integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw== -eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1: - version "3.4.1" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.1.tgz#c22c48f48942d08ca824cc526211ae400478a994" - integrity sha512-pZnmmLwYzf+kWaM/Qgrvpen51upAktaaiI01nsJD/Yr3lMOdNtq0cxkrrg16w64VtisN6okbs7Q8AfGqj4c9fA== +eslint-visitor-keys@^3.3.0, eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.2: + version "3.4.2" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.2.tgz#8c2095440eca8c933bedcadf16fefa44dbe9ba5f" + integrity sha512-8drBzUEyZ2llkpCA67iYrgEssKDUu68V8ChqqOfFupIaG/LCVPUT+CoGJpT77zJprs4T/W7p07LP7zAIMuweVw== -eslint@8.45.0: - version "8.45.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.45.0.tgz#bab660f90d18e1364352c0a6b7c6db8edb458b78" - integrity sha512-pd8KSxiQpdYRfYa9Wufvdoct3ZPQQuVuU5O6scNgMuOMYuxvH0IGaYK0wUFjo4UYYQQCUndlXiMbnxopwvvTiw== +eslint@8.46.0: + version "8.46.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.46.0.tgz#a06a0ff6974e53e643acc42d1dcf2e7f797b3552" + integrity sha512-cIO74PvbW0qU8e0mIvk5IV3ToWdCq5FYG6gWPHHkx6gNdjlbAYvtfHmlCMXxjcoVaIdwy/IAt3+mDkZkfvb2Dg== dependencies: "@eslint-community/eslint-utils" "^4.2.0" - "@eslint-community/regexpp" "^4.4.0" - "@eslint/eslintrc" "^2.1.0" - "@eslint/js" "8.44.0" + "@eslint-community/regexpp" "^4.6.1" + "@eslint/eslintrc" "^2.1.1" + "@eslint/js" "^8.46.0" "@humanwhocodes/config-array" "^0.11.10" "@humanwhocodes/module-importer" "^1.0.1" "@nodelib/fs.walk" "^1.2.8" - ajv "^6.10.0" + ajv "^6.12.4" chalk "^4.0.0" cross-spawn "^7.0.2" debug "^4.3.2" doctrine "^3.0.0" escape-string-regexp "^4.0.0" - eslint-scope "^7.2.0" - eslint-visitor-keys "^3.4.1" - espree "^9.6.0" + eslint-scope "^7.2.2" + eslint-visitor-keys "^3.4.2" + espree "^9.6.1" esquery "^1.4.2" esutils "^2.0.2" fast-deep-equal "^3.1.3" @@ -3578,7 +3559,7 @@ eslint@8.45.0: strip-ansi "^6.0.1" text-table "^0.2.0" -espree@^9.6.0: +espree@^9.6.0, espree@^9.6.1: version "9.6.1" resolved "https://registry.yarnpkg.com/espree/-/espree-9.6.1.tgz#a2a17b8e434690a5432f2f8018ce71d331a48c6f" integrity sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ== @@ -4363,10 +4344,10 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055" integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA== -is-core-module@^2.1.0, is-core-module@^2.11.0, is-core-module@^2.12.0, is-core-module@^2.12.1: - version "2.12.1" - resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.12.1.tgz#0c0b6885b6f80011c71541ce15c8d66cf5a4f9fd" - integrity sha512-Q4ZuBAe2FUsKtyQJoQHlvP8OvBERxO3jEmy1I7hcRXcJBGGHFh/aJBswbXuS9sgrDH2QUO8ilkwNPHvHMd8clg== +is-core-module@^2.1.0, is-core-module@^2.11.0, is-core-module@^2.12.1, is-core-module@^2.13.0: + version "2.13.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db" + integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ== dependencies: has "^1.0.3" @@ -5550,7 +5531,7 @@ node-int64@^0.4.0: resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" integrity sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw== -node-releases@^2.0.12: +node-releases@^2.0.13: version "2.0.13" resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.13.tgz#d5ed1627c23e3461e819b02e57b75e4899b1c81d" integrity sha512-uYr7J37ae/ORWdZeQ1xxMJe3NtdmqMC/JZK+geofDrkLUApKRHPd18/TxtBOJ4A0/+uUIliorNrfYV6s1b02eQ== @@ -6125,15 +6106,15 @@ regenerate@^1.4.2: resolved "https://registry.yarnpkg.com/regenerate/-/regenerate-1.4.2.tgz#b9346d8827e8f5a32f7ba29637d398b69014848a" integrity sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A== -regenerator-runtime@^0.13.11: - version "0.13.11" - resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" - integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== +regenerator-runtime@^0.14.0: + version "0.14.0" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz#5e19d68eb12d486f797e15a3c6a918f7cec5eb45" + integrity sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA== -regenerator-transform@^0.15.1: - version "0.15.1" - resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.1.tgz#f6c4e99fc1b4591f780db2586328e4d9a9d8dc56" - integrity sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg== +regenerator-transform@^0.15.2: + version "0.15.2" + resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.15.2.tgz#5bbae58b522098ebdf09bca2f83838929001c7a4" + integrity sha512-hfMp2BoF0qOk3uc5V20ALGDS2ddjQaLrdl7xrGXvAIow7qeWRM2VA2HuCHkUKk9slq3VwEwLNK3DFBqDfPGYtg== dependencies: "@babel/runtime" "^7.8.4" @@ -6214,21 +6195,12 @@ resolve.exports@^2.0.0: resolved "https://registry.yarnpkg.com/resolve.exports/-/resolve.exports-2.0.2.tgz#f8c934b8e6a13f539e38b7098e2e36134f01e800" integrity sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg== -resolve@^1.1.4, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.22.1, resolve@^1.4.0: - version "1.22.2" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.2.tgz#0ed0943d4e301867955766c9f3e1ae6d01c6845f" - integrity sha512-Sb+mjNHOULsBv818T40qSPeRiuWLyaGMa5ewydRLFimneixmVy2zdivRl+AF6jaYPC8ERxGDmFSiqui6SfPd+g== +resolve@^1.1.4, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.20.0, resolve@^1.22.3, resolve@^1.22.4, resolve@^1.4.0: + version "1.22.4" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.4.tgz#1dc40df46554cdaf8948a486a10f6ba1e2026c34" + integrity sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg== dependencies: - is-core-module "^2.11.0" - path-parse "^1.0.7" - supports-preserve-symlinks-flag "^1.0.0" - -resolve@^1.22.3: - version "1.22.3" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.3.tgz#4b4055349ffb962600972da1fdc33c46a4eb3283" - integrity sha512-P8ur/gp/AmbEzjr729bZnLjXK5Z+4P0zhIJgBgzqRih7hL7BOukHGtSTA3ACMY467GRFz3duQsi0bDZdR7DKdw== - dependencies: - is-core-module "^2.12.0" + is-core-module "^2.13.0" path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" @@ -6428,9 +6400,9 @@ signal-exit@^3.0.3, signal-exit@^3.0.7: integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== signal-exit@^4.0.1: - version "4.0.2" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.0.2.tgz#ff55bb1d9ff2114c13b400688fa544ac63c36967" - integrity sha512-MY2/qGx4enyjprQnFaZsHib3Yadh3IXyV2C321GY0pjGfVBu4un0uDJkwgdxqO+Rdx8JMT8IfJIRwbYVz3Ob3Q== + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== simple-concat@^1.0.0: version "1.0.1" @@ -7041,9 +7013,9 @@ typedoc-plugin-mdn-links@^3.0.3: integrity sha512-NXhIpwQnsg7BcyMCHVqj3tUK+DL4g3Bt96JbFl4APzTGFkA+iM6GfZ/fn3TAqJ8O0CXG5R9BfWxolw1m1omNuQ== typedoc-plugin-missing-exports@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.0.0.tgz#9bdc4e30b0c7f24e9f1cb8890db4d01f608717c5" - integrity sha512-t0QlKCm27/8DaheJkLo/gInSNjzBXgSciGhoLpL6sLyXZibm7SuwJtHvg4qXI2IjJfFBgW9mJvvszpoxMyB0TA== + version "2.0.1" + resolved "https://registry.yarnpkg.com/typedoc-plugin-missing-exports/-/typedoc-plugin-missing-exports-2.0.1.tgz#76b7d499b2b6fe6fd119b02a0726391c0fe17da8" + integrity sha512-+A78kT78uC0Dbv2EFB9RXHO3iywJ5x89jd4z0bLL7Z8DlOSQjJxhRHf8otXsHZbgRUWAuYqYMA9LzxfuSOc87A== typedoc-plugin-versions-cli@^0.1.12: version "0.1.12" From 8d4dbef7ca4b1c25252a4557ef59409b71163beb Mon Sep 17 00:00:00 2001 From: valere Date: Wed, 16 Aug 2023 14:45:27 +0200 Subject: [PATCH 13/19] code review --- src/rust-crypto/backup.ts | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/rust-crypto/backup.ts b/src/rust-crypto/backup.ts index 4d84d80e7b4..2f0bcd0082d 100644 --- a/src/rust-crypto/backup.ts +++ b/src/rust-crypto/backup.ts @@ -46,7 +46,9 @@ export class RustBackupManager extends TypedEventEmittererr).data) { - const errCode = (err).data.errcode; + if (err instanceof MatrixError) { + const errCode = err.data.errcode; if (errCode == "M_NOT_FOUND" || errCode == "M_WRONG_ROOM_KEYS_VERSION") { await this.disableKeyBackup(); - this.emit(CryptoEvent.KeyBackupFailed, (err).data.errcode!); + this.emit(CryptoEvent.KeyBackupFailed, err.data.errcode!); // There was an active backup and we are out of sync with the server // force a check server side this.backupKeysLoopRunning = false; @@ -236,7 +238,7 @@ export class RustBackupManager extends TypedEventEmittererr).data.retry_after_ms; + const waitTime = err.data.retry_after_ms; if (waitTime > 0) { sleep(waitTime); continue; @@ -244,7 +246,7 @@ export class RustBackupManager extends TypedEventEmitter Date: Wed, 16 Aug 2023 14:52:38 +0200 Subject: [PATCH 14/19] Generate test data for exported keys --- spec/integ/crypto/megolm-backup.spec.ts | 11 ++--- .../test-data/generate-test-data.py | 46 ++++++++++++++++++- spec/test-utils/test-data/index.ts | 42 ++++++++++++++++- spec/test-utils/test-data/static-test-data.ts | 46 ------------------- spec/unit/rust-crypto/rust-crypto.spec.ts | 3 +- 5 files changed, 91 insertions(+), 57 deletions(-) delete mode 100644 spec/test-utils/test-data/static-test-data.ts diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index bd1a51417d3..ab63637623d 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -26,7 +26,6 @@ import { E2EKeyResponder } from "../../test-utils/E2EKeyResponder"; import { mockInitialApiRequests } from "../../test-utils/mockEndpoints"; import { awaitDecryption, CRYPTO_BACKENDS, InitCrypto, syncPromise } from "../../test-utils/test-utils"; import * as testData from "../../test-utils/test-data"; -import * as staticTestData from "../../test-utils/test-data/static-test-data"; import { KeyBackupInfo } from "../../../src/crypto-api/keybackup"; import { IKeyBackup } from "../../../src/crypto/backup"; @@ -252,7 +251,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); }); - const someRoomKeys = staticTestData.MEGOLM_SESSION_DATA_ARRAY; + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; const uploadMockEmitter = mockUploadEmitter(testData.SIGNED_BACKUP_DATA.version!); @@ -288,7 +287,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe await remainingZeroPromise; // A new key import should trigger a new upload. - const newKey = staticTestData.MEGOLM_SESSION_DATA; + const newKey = testData.MEGOLM_SESSION_DATA; const newKeyUploadPromise = new Promise((resolve) => { uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => { @@ -326,7 +325,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); }); - const someRoomKeys = staticTestData.MEGOLM_SESSION_DATA_ARRAY; + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; fetchMock.get("path:/_matrix/client/v3/room_keys/version", testData.SIGNED_BACKUP_DATA, { overwriteRoutes: true, @@ -384,7 +383,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); // A new key import should trigger a new upload. - const newKey = staticTestData.MEGOLM_SESSION_DATA; + const newKey = testData.MEGOLM_SESSION_DATA; const newKeyUploadPromise = new Promise((resolve) => { uploadMockEmitter.on(MockKeyUploadEvent.KeyUploaded, (roomId, sessionId, version) => { @@ -435,7 +434,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }); // kick the import loop off and wait for the failed request - const someRoomKeys = staticTestData.MEGOLM_SESSION_DATA_ARRAY; + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; await aliceCrypto.importRoomKeys(someRoomKeys); const result = await aliceCrypto.checkKeyBackupAndEnable(); diff --git a/spec/test-utils/test-data/generate-test-data.py b/spec/test-utils/test-data/generate-test-data.py index 9bc63dbd0ea..6044719debe 100755 --- a/spec/test-utils/test-data/generate-test-data.py +++ b/spec/test-utils/test-data/generate-test-data.py @@ -30,6 +30,7 @@ from canonicaljson import encode_canonical_json from cryptography.hazmat.primitives.asymmetric import ed25519, x25519 from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat +from random import randbytes # input data TEST_USER_ID = "@alice:localhost" @@ -123,7 +124,7 @@ def main() -> None: * Do not edit by hand! This file is generated by `./generate-test-data.py` */ -import {{ IDeviceKeys }} from "../../../src/@types/crypto"; +import {{ IDeviceKeys, IMegolmSessionData }} from "../../../src/@types/crypto"; import {{ IDownloadKeyResult }} from "../../../src"; import {{ KeyBackupInfo }} from "../../../src/crypto-api"; @@ -167,6 +168,20 @@ def main() -> None: /** Signed backup data, suitable for return from `GET /_matrix/client/v3/room_keys/keys/{{roomId}}/{{sessionId}}` */ export const SIGNED_BACKUP_DATA: KeyBackupInfo = { json.dumps(backup_data, indent=4) }; + +export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [ + { + json.dumps(build_exported_megolm_key(), indent=8) + }, + { + json.dumps(build_exported_megolm_key(), indent=8) + }, +]; + +/** An exported megolm session */ +export const MEGOLM_SESSION_DATA: IMegolmSessionData = { + json.dumps(build_exported_megolm_key(), indent=4) +}; """, end="", ) @@ -265,6 +280,35 @@ def sign_json(json_object: dict, private_key: ed25519.Ed25519PrivateKey) -> str: return signature_base64 +def build_exported_megolm_key() -> dict: + index = int(0) + private_key = ed25519.Ed25519PrivateKey.generate() + # Just use radom bytes for the ratchet parts + ratchet = randbytes(32 * 4) + # exported key, start with version byte + exported_key = bytearray(b'\x01') + exported_key += index.to_bytes(4, 'big') + exported_key += ratchet + # KPub + exported_key += private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw) + + + megolm_export = { + "algorithm": "m.megolm.v1.aes-sha2", + "room_id": "!roomA:example.org", + "sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", + "session_id": encode_base64( + private_key.public_key().public_bytes(Encoding.Raw, PublicFormat.Raw) + ), + "session_key": encode_base64(exported_key), + "sender_claimed_keys": { + "ed25519": encode_base64(ed25519.Ed25519PrivateKey.generate().public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)), + }, + "forwarding_curve25519_key_chain": [], + } + + return megolm_export + if __name__ == "__main__": main() diff --git a/spec/test-utils/test-data/index.ts b/spec/test-utils/test-data/index.ts index 5a2e3fa9d68..320dbc3bdab 100644 --- a/spec/test-utils/test-data/index.ts +++ b/spec/test-utils/test-data/index.ts @@ -3,7 +3,7 @@ * Do not edit by hand! This file is generated by `./generate-test-data.py` */ -import { IDeviceKeys } from "../../../src/@types/crypto"; +import { IDeviceKeys, IMegolmSessionData } from "../../../src/@types/crypto"; import { IDownloadKeyResult } from "../../../src"; import { KeyBackupInfo } from "../../../src/crypto-api"; @@ -115,4 +115,42 @@ export const SIGNED_BACKUP_DATA: KeyBackupInfo = { } } } -}; \ No newline at end of file +}; + +export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [ + { + "algorithm": "m.megolm.v1.aes-sha2", + "room_id": "!roomA:example.org", + "sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", + "session_id": "B/1owFh7K0r8cAVzQJnYxVpSmR+Cutiy7DTLRPsIsOI", + "session_key": "AQAAAADdFxBB2aZSfIHX9uOy1k9RHAtOYavhB2xBG1BiN5fXLTnFj9WMNGw1puBHbc9Xz1OiO79AUHOj/Yz1aiqn/PQBoBY7n7onN3vRrEUOk8PXpUfKGMwDN6IX1eyXD9jg36CQCTp2rtGZIitT4uO1rEC1vyI1F/9FQ+zFS1FKBTnAoQf9aMBYeytK/HAFc0CZ2MVaUpkfgrrYsuw0y0T7CLDi", + "sender_claimed_keys": { + "ed25519": "0Z+1+PWGErCmWx9fszojigz8Axh51Hgu9l8apPKU80k" + }, + "forwarding_curve25519_key_chain": [] +}, + { + "algorithm": "m.megolm.v1.aes-sha2", + "room_id": "!roomA:example.org", + "sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", + "session_id": "mG/goAa0BeALT2sjRL8bVTvYow1RfTFmcVRU22S1W4k", + "session_key": "AQAAAADlTISccgGBYg/MSQnMlBve5GWzxckGQ0tpiDSKARDF8JhOC7GJxhanJtBT/qbiiEdo/b+cfNjLM2p5fld1R+6v1vzPCrOD2wDH99n46FJAncHXZWjiaNKy35EdZK+v75BkTqfoNluawv4n3qgHa8Z8DK0jLFOfwXG3okTZX28fDJhv4KAGtAXgC09rI0S/G1U72KMNUX0xZnFUVNtktVuJ", + "sender_claimed_keys": { + "ed25519": "XXGabP9k45NJ1cWRcZ3NyLAkQ7BKNmSoW5MjhfsnqYY" + }, + "forwarding_curve25519_key_chain": [] +}, +]; + +/** An exported megolm session */ +export const MEGOLM_SESSION_DATA: IMegolmSessionData = { + "algorithm": "m.megolm.v1.aes-sha2", + "room_id": "!roomA:example.org", + "sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", + "session_id": "0GDRENodidyou+W+od+Y8AyHLJe+7Jo+rzDwjSPDFH8", + "session_key": "AQAAAAB+5PGbWI5AOqKXrwGYd8pDOFVbiL4vzLSzVqMm2IrlR6SUxC6zYS2FUxKTaJLnnZKblsSOwMj5Uz7L7ld9FhZ4JTKpb+KtekVmohGjmzEvWKNBnPoqZRRg6gD25FnnxYImXwAhuBl04TG76P4FRzMCb4AlHIOYiQf5b8EIGU6h3tBg0RDaHYncqLvlvqHfmPAMhyyXvuyaPq8w8I0jwxR/", + "sender_claimed_keys": { + "ed25519": "zFsHIa5597r9zods42B3KD1laMBdlaTBgwp+rgkh14o" + }, + "forwarding_curve25519_key_chain": [] +}; diff --git a/spec/test-utils/test-data/static-test-data.ts b/spec/test-utils/test-data/static-test-data.ts deleted file mode 100644 index 89e94094846..00000000000 --- a/spec/test-utils/test-data/static-test-data.ts +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Static Test data for cryptography tests - */ - -import { IMegolmSessionData } from "../../../src/@types/crypto"; - -export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [ - { - algorithm: "m.megolm.v1.aes-sha2", - room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480", - sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY", - session_id: "iGQG5GaP1/B3dSH6zCQDQqrNuotrtQjVC7w1OsUDwbg", - session_key: - "AQAAAADaCbP2gdOy8jrhikjploKgSBaFSJ5rvHcziaADbwNEzeCSrfuAUlXvCvxik8kU+MfCHIi5arN2M7UM5rGKdzkHnkReoIByFkeMdbjKWk5SFpVQexcM74eDhBGj+ICkQqOgApfnEbSswrmreB0+MhHHyLStwW5fy5f8A9QW1sbPuohkBuRmj9fwd3Uh+swkA0KqzbqLa7UI1Qu8NTrFA8G4", - sender_claimed_keys: { - ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k", - }, - forwarding_curve25519_key_chain: [], - }, - { - algorithm: "m.megolm.v1.aes-sha2", - room_id: "!cLDYAnjpiQXIrSwngM:localhost:8480", - sender_key: "C9FMqTD20C0VaGWE/aSImkimuE6HDa/RyYj5gRUg3gY", - session_id: "P/Jy9Tog4CMtLseeS4Fe2AEXZov3k6cibcop/uyhr78", - session_key: - "AQAAAAATyAVm0c9c9DW9Od72MxvfSDYoysBw3C6yMJ3bYuTmssHN7yNGm59KCtKeFp2Y5qO7lvUmwOfSTvTASUb7HViE7Lt+Bvp5WiMTJ2Pv6m+N12ihyowV5lgtKFWI18Wxd0AugMTVQRwjBK6aMobf86NXWD2hiKm3N6kWbC0PXmqV7T/ycvU6IOAjLS7HnkuBXtgBF2aL95OnIm3KKf7soa+/", - sender_claimed_keys: { - ed25519: "RSq0Xw0RR0DeqlJ/j3qrF5qbN0D96fKk8lz9kZJlG9k", - }, - forwarding_curve25519_key_chain: [], - }, -]; - -export const MEGOLM_SESSION_DATA: IMegolmSessionData = { - algorithm: "m.megolm.v1.aes-sha2", - sender_key: "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", - sender_claimed_keys: { - ed25519: "vEjK0zoE/flKjhAbzzOePv5AWXUjzcfkKdTjLpyKuhI", - }, - room_id: "!EclSXmduSDvVcwKLnn:example.org", - session_id: "QYiBwpB7nf0lcLMR7T9DPaQA5ml4Mhh0WfjAWrDWyOY", - session_key: - "AQAAAAAkbP0ON4cmpKBZuUXBDlfz0iSowKc/9FYzWIQVmdQM3tk5YDUaO+9kmp0T00XImkgbKCq2CFcoaQAITVU5qqNTi04P9ntakp6A3RoLn7VCiJd9RPfwaiSMeKQakGyFotkXkaiUKcPCHgEslVo1SmnaEcKLuxn9gEueHCK3hYvwTUGIgcKQe539JXCzEe0/Qz2kAOZpeDIYdFn4wFqw1sjm", - forwarding_curve25519_key_chain: [], - first_known_index: 0, -}; diff --git a/spec/unit/rust-crypto/rust-crypto.spec.ts b/spec/unit/rust-crypto/rust-crypto.spec.ts index ee544048ca0..0985256731e 100644 --- a/spec/unit/rust-crypto/rust-crypto.spec.ts +++ b/spec/unit/rust-crypto/rust-crypto.spec.ts @@ -40,7 +40,6 @@ import { OutgoingRequestProcessor } from "../../../src/rust-crypto/OutgoingReque import { ServerSideSecretStorage } from "../../../src/secret-storage"; import { CryptoCallbacks, ImportRoomKeysOpts, VerificationRequest } from "../../../src/crypto-api"; import * as testData from "../../test-utils/test-data"; -import * as staticTestData from "../../test-utils/test-data/static-test-data"; const TEST_USER = "@alice:example.com"; const TEST_DEVICE_ID = "TEST_DEVICE"; @@ -62,7 +61,7 @@ describe("RustCrypto", () => { ); it("should import and export keys", async () => { - const someRoomKeys = staticTestData.MEGOLM_SESSION_DATA_ARRAY; + const someRoomKeys = testData.MEGOLM_SESSION_DATA_ARRAY; let importTotal = 0; const opt: ImportRoomKeysOpts = { progressCallback: (stage) => { From 5d892bda31c8e9a9dfedc3b209ab7778576f5598 Mon Sep 17 00:00:00 2001 From: valere Date: Thu, 17 Aug 2023 11:18:05 +0200 Subject: [PATCH 15/19] code review cleaning --- spec/integ/crypto/megolm-backup.spec.ts | 2 +- .../test-data/generate-test-data.py | 31 ++++++++++++------- spec/test-utils/test-data/index.ts | 23 +++++++------- src/crypto/backup.ts | 2 +- 4 files changed, 33 insertions(+), 25 deletions(-) diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index ab63637623d..302bc1fe236 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -93,7 +93,7 @@ type MockKeyUploadEventHandlerMap = { }; /* - * Test helper. Will emit an event everytime fetchmock sees a request to backup a key. + * Test helper. Returns an event emitter that will emit an event every time fetchmock sees a request to backup a key. */ function mockUploadEmitter( expectedVersion: string, diff --git a/spec/test-utils/test-data/generate-test-data.py b/spec/test-utils/test-data/generate-test-data.py index 6044719debe..8a7873166e9 100755 --- a/spec/test-utils/test-data/generate-test-data.py +++ b/spec/test-utils/test-data/generate-test-data.py @@ -30,7 +30,7 @@ from canonicaljson import encode_canonical_json from cryptography.hazmat.primitives.asymmetric import ed25519, x25519 from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat -from random import randbytes +from random import randbytes, seed # input data TEST_USER_ID = "@alice:localhost" @@ -117,6 +117,10 @@ def main() -> None: TEST_USER_ID: {f"ed25519:{TEST_DEVICE_ID}": sig} } + set_of_exported_room_keys = [build_exported_megolm_key(), build_exported_megolm_key()] + + additional_exported_room_key = build_exported_megolm_key() + print( f"""\ /* Test data for cryptography tests @@ -169,24 +173,23 @@ def main() -> None: /** Signed backup data, suitable for return from `GET /_matrix/client/v3/room_keys/keys/{{roomId}}/{{sessionId}}` */ export const SIGNED_BACKUP_DATA: KeyBackupInfo = { json.dumps(backup_data, indent=4) }; -export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [ - { - json.dumps(build_exported_megolm_key(), indent=8) - }, - { - json.dumps(build_exported_megolm_key(), indent=8) - }, -]; +/** A set of megolm keys that can be imported via CryptoAPI#importRoomKeys */ +export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = { + json.dumps(set_of_exported_room_keys, indent=4) +}; /** An exported megolm session */ export const MEGOLM_SESSION_DATA: IMegolmSessionData = { - json.dumps(build_exported_megolm_key(), indent=4) + json.dumps(additional_exported_room_key, indent=4) }; """, end="", ) +# Use static seed to have stable random test data upon new generation +seed(10) + def build_cross_signing_keys_data() -> dict: """Build the signed cross-signing-keys data for return from /keys/query""" master_private_key = ed25519.Ed25519PrivateKey.from_private_bytes( @@ -281,8 +284,12 @@ def sign_json(json_object: dict, private_key: ed25519.Ed25519PrivateKey) -> str: return signature_base64 def build_exported_megolm_key() -> dict: + """ + Creates an exported megolm room key, as per https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-export-format + that can be imported via importRoomKeys API. + """ index = int(0) - private_key = ed25519.Ed25519PrivateKey.generate() + private_key = ed25519.Ed25519PrivateKey.from_private_bytes(randbytes(32)) # Just use radom bytes for the ratchet parts ratchet = randbytes(32 * 4) # exported key, start with version byte @@ -302,7 +309,7 @@ def build_exported_megolm_key() -> dict: ), "session_key": encode_base64(exported_key), "sender_claimed_keys": { - "ed25519": encode_base64(ed25519.Ed25519PrivateKey.generate().public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)), + "ed25519": encode_base64(ed25519.Ed25519PrivateKey.from_private_bytes(randbytes(32)).public_key().public_bytes(Encoding.Raw, PublicFormat.Raw)), }, "forwarding_curve25519_key_chain": [], } diff --git a/spec/test-utils/test-data/index.ts b/spec/test-utils/test-data/index.ts index 320dbc3bdab..8212068fb62 100644 --- a/spec/test-utils/test-data/index.ts +++ b/spec/test-utils/test-data/index.ts @@ -117,29 +117,30 @@ export const SIGNED_BACKUP_DATA: KeyBackupInfo = { } }; +/** A set of megolm keys that can be imported via CryptoAPI#importRoomKeys */ export const MEGOLM_SESSION_DATA_ARRAY: IMegolmSessionData[] = [ { "algorithm": "m.megolm.v1.aes-sha2", "room_id": "!roomA:example.org", "sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", - "session_id": "B/1owFh7K0r8cAVzQJnYxVpSmR+Cutiy7DTLRPsIsOI", - "session_key": "AQAAAADdFxBB2aZSfIHX9uOy1k9RHAtOYavhB2xBG1BiN5fXLTnFj9WMNGw1puBHbc9Xz1OiO79AUHOj/Yz1aiqn/PQBoBY7n7onN3vRrEUOk8PXpUfKGMwDN6IX1eyXD9jg36CQCTp2rtGZIitT4uO1rEC1vyI1F/9FQ+zFS1FKBTnAoQf9aMBYeytK/HAFc0CZ2MVaUpkfgrrYsuw0y0T7CLDi", + "session_id": "FYOoKQSwe4d9jhTZ/LQCZFJINjPEqZ7Or4Z08reP92M", + "session_key": "AQAAAABZ0jXQOprFfXe41tIFmAtHxflJp4O2hM/vzQQpOazOCFeWSoW5P3Z9Q+voU3eXehMwyP8/hm/Q8xLP6/PmJdy+71se/17kdFwcDGgLxBWfa4ODM9zlI4EjKbNqmiii5loJ7rBhA/XXaw80m0hfU6zTDX/KrO55J0Pt4vJ0LDa3LBWDqCkEsHuHfY4U2fy0AmRSSDYzxKmezq+GdPK3j/dj", "sender_claimed_keys": { - "ed25519": "0Z+1+PWGErCmWx9fszojigz8Axh51Hgu9l8apPKU80k" + "ed25519": "QdgHgdpDgihgovpPzUiThXur1fbErTFh7paFvNKSgN0" }, "forwarding_curve25519_key_chain": [] -}, + }, { "algorithm": "m.megolm.v1.aes-sha2", "room_id": "!roomA:example.org", "sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", - "session_id": "mG/goAa0BeALT2sjRL8bVTvYow1RfTFmcVRU22S1W4k", - "session_key": "AQAAAADlTISccgGBYg/MSQnMlBve5GWzxckGQ0tpiDSKARDF8JhOC7GJxhanJtBT/qbiiEdo/b+cfNjLM2p5fld1R+6v1vzPCrOD2wDH99n46FJAncHXZWjiaNKy35EdZK+v75BkTqfoNluawv4n3qgHa8Z8DK0jLFOfwXG3okTZX28fDJhv4KAGtAXgC09rI0S/G1U72KMNUX0xZnFUVNtktVuJ", + "session_id": "mPYSGA2l1tOQiipEDEVYhDSdTSFh2lDW1qpGKYZRxTc", + "session_key": "AQAAAAAHwgkB49BTPAEGTCK6degxUIbl8GPG2ugPRYhNtOpNic63u11+baXFfjDw5fmVfD1gJXpQQjGsqrIYioxrB1xzl7mfb942UHhYdaMQZowpp1fSpJVsxR5TddUU2EWifYD9EQsoz8mY1zqoazm4vUP4v9yxaTcUBj2c6HMJCY0gCJj2EhgNpdbTkIoqRAxFWIQ0nU0hYdpQ1taqRimGUcU3", "sender_claimed_keys": { - "ed25519": "XXGabP9k45NJ1cWRcZ3NyLAkQ7BKNmSoW5MjhfsnqYY" + "ed25519": "IrkbT6H+0urDf6wKDSyVC1fh1t84Vz6T62snni86Cog" }, "forwarding_curve25519_key_chain": [] -}, + } ]; /** An exported megolm session */ @@ -147,10 +148,10 @@ export const MEGOLM_SESSION_DATA: IMegolmSessionData = { "algorithm": "m.megolm.v1.aes-sha2", "room_id": "!roomA:example.org", "sender_key": "/Bu9e34hUClhddpf4E5gu5qEAdMY31+1A9HbiAeeQgo", - "session_id": "0GDRENodidyou+W+od+Y8AyHLJe+7Jo+rzDwjSPDFH8", - "session_key": "AQAAAAB+5PGbWI5AOqKXrwGYd8pDOFVbiL4vzLSzVqMm2IrlR6SUxC6zYS2FUxKTaJLnnZKblsSOwMj5Uz7L7ld9FhZ4JTKpb+KtekVmohGjmzEvWKNBnPoqZRRg6gD25FnnxYImXwAhuBl04TG76P4FRzMCb4AlHIOYiQf5b8EIGU6h3tBg0RDaHYncqLvlvqHfmPAMhyyXvuyaPq8w8I0jwxR/", + "session_id": "ipdI6Zs/7DzFTEhiA2iGaMDfHkIYCleqXT6L+5e1/co", + "session_key": "AQAAAABXGO+Z9jlQJhIL6ByhXrv2BwCIxkhh7MXpKLsYmXkJcWrQlirmXmD79ga1zo+I4DCtEZzyGSpDWXBC6G7ez3H4gDMBam1RE3Jm5tc+oTlIri32UkYgSL0kBkcEnttqmIXBlK8tAfJo3cJnlh7F4ltEOAqrdME6dU0zXTkqXmURqYqXSOmbP+w8xUxIYgNohmjA3x5CGApXql0+i/uXtf3K", "sender_claimed_keys": { - "ed25519": "zFsHIa5597r9zods42B3KD1laMBdlaTBgwp+rgkh14o" + "ed25519": "Bhbpt6hqMZlSH4sJV7xiEEEiPVeTWz4Vkujl1EMdIPI" }, "forwarding_curve25519_key_chain": [] }; diff --git a/src/crypto/backup.ts b/src/crypto/backup.ts index 5542b297171..a57a5eb389b 100644 --- a/src/crypto/backup.ts +++ b/src/crypto/backup.ts @@ -499,7 +499,7 @@ export class BackupManager { } } } catch (err) { - // No one actually check errors on this promise, it's spawned internally. + // No one actually checks errors on this promise, it's spawned internally. // Just log, apps/client should use events to check status logger.log(`Backup loop failed ${err}`); } finally { From cdde54af4eea0b4dfc5e4b0ddd8ce5150bb18550 Mon Sep 17 00:00:00 2001 From: valere Date: Thu, 17 Aug 2023 11:30:38 +0200 Subject: [PATCH 16/19] cleanup legacy backup loop --- src/crypto/backup.ts | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/crypto/backup.ts b/src/crypto/backup.ts index a57a5eb389b..e3f3cf4b1e2 100644 --- a/src/crypto/backup.ts +++ b/src/crypto/backup.ts @@ -453,6 +453,7 @@ export class BackupManager { await sleep(delay); if (!this.clientRunning) { logger.debug("Key backup send aborted, client stopped"); + this.sendingBackups = false; return; } let numFailures = 0; // number of consecutive failures @@ -464,27 +465,26 @@ export class BackupManager { const numBackedUp = await this.backupPendingKeys(KEY_BACKUP_KEYS_PER_REQUEST); if (numBackedUp === 0) { // no sessions left needing backup: we're done + this.sendingBackups = false; return; } numFailures = 0; } catch (err) { numFailures++; logger.log("Key backup request failed", err); - if ((err).data) { - if ( - (err).data.errcode == "M_NOT_FOUND" || - (err).data.errcode == "M_WRONG_ROOM_KEYS_VERSION" - ) { - // fix do it now as the await check might trigger a new call to the loop before - // the finally is called + if (err instanceof MatrixError) { + const errCode = err.data.errcode; + if (errCode == "M_NOT_FOUND" || errCode == "M_WRONG_ROOM_KEYS_VERSION") { + // Set to false now as `checkKeyBackup` might schedule a backupsend before this one ends. this.sendingBackups = false; + // Backup version has changed or this backup version + // has been deleted + this.baseApis.crypto!.emit(CryptoEvent.KeyBackupFailed, errCode); // Re-check key backup status on error, so we can be // sure to present the current situation when asked. + // This call might restart the backup loop if new backup version is trusted await this.checkKeyBackup(); - // Backup version has changed or this backup version - // has been deleted - this.baseApis.crypto!.emit(CryptoEvent.KeyBackupFailed, (err).data.errcode!); - throw err; + return; } } } @@ -495,6 +495,7 @@ export class BackupManager { if (!this.clientRunning) { logger.debug("Key backup send loop aborted, client stopped"); + this.sendingBackups = false; return; } } @@ -502,7 +503,6 @@ export class BackupManager { // No one actually checks errors on this promise, it's spawned internally. // Just log, apps/client should use events to check status logger.log(`Backup loop failed ${err}`); - } finally { this.sendingBackups = false; } } From d1778baad8d74cd94fad81a0727573d76e4078ac Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 17 Aug 2023 13:35:50 +0200 Subject: [PATCH 17/19] Update spec/test-utils/test-data/generate-test-data.py Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- spec/test-utils/test-data/generate-test-data.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/test-utils/test-data/generate-test-data.py b/spec/test-utils/test-data/generate-test-data.py index 8a7873166e9..5d4f458fe80 100755 --- a/spec/test-utils/test-data/generate-test-data.py +++ b/spec/test-utils/test-data/generate-test-data.py @@ -285,8 +285,8 @@ def sign_json(json_object: dict, private_key: ed25519.Ed25519PrivateKey) -> str: def build_exported_megolm_key() -> dict: """ - Creates an exported megolm room key, as per https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-export-format - that can be imported via importRoomKeys API. + Creates an exported megolm room key, as per https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-export-format + that can be imported via importRoomKeys API. """ index = int(0) private_key = ed25519.Ed25519PrivateKey.from_private_bytes(randbytes(32)) From 5402e0e61fdb647a0700865bb3a5d8731306d358 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 17 Aug 2023 13:35:59 +0200 Subject: [PATCH 18/19] Update spec/test-utils/test-data/generate-test-data.py Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- spec/test-utils/test-data/generate-test-data.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/test-utils/test-data/generate-test-data.py b/spec/test-utils/test-data/generate-test-data.py index 5d4f458fe80..a3b8b134660 100755 --- a/spec/test-utils/test-data/generate-test-data.py +++ b/spec/test-utils/test-data/generate-test-data.py @@ -288,7 +288,7 @@ def build_exported_megolm_key() -> dict: Creates an exported megolm room key, as per https://gitlab.matrix.org/matrix-org/olm/blob/master/docs/megolm.md#session-export-format that can be imported via importRoomKeys API. """ - index = int(0) + index = 0 private_key = ed25519.Ed25519PrivateKey.from_private_bytes(randbytes(32)) # Just use radom bytes for the ratchet parts ratchet = randbytes(32 * 4) From 48f5605a735480f8e50c20ee91f1e960dbc00dfe Mon Sep 17 00:00:00 2001 From: valere Date: Thu, 17 Aug 2023 16:00:20 +0200 Subject: [PATCH 19/19] update yarn.lock for new wasm bindings --- yarn.lock | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/yarn.lock b/yarn.lock index 2fc39092e42..d737daa22ac 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1463,14 +1463,13 @@ "@jridgewell/resolve-uri" "^3.1.0" "@jridgewell/sourcemap-codec" "^1.4.14" -"@matrix-org/matrix-sdk-crypto-wasm@^1.2.0": +"@matrix-org/matrix-sdk-crypto-wasm@^1.2.1": version "1.2.1" resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-wasm/-/matrix-sdk-crypto-wasm-1.2.1.tgz#5b546c8a0e53b614f10b77b3b649818aed9d0db1" integrity sha512-DCb7Q83PCQK0uav5vB3KNV/hJPlxAhT/ddar+VHz2kC39hMLKGzWYVhprpLYVcavaE/6OX+Q/xFkAoV/3QtUHQ== "@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz": version "3.2.14" - uid acd96c00a881d0f462e1f97a56c73742c8dbc984 resolved "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz#acd96c00a881d0f462e1f97a56c73742c8dbc984" "@microsoft/tsdoc-config@0.16.2":