From 65e87950835d4d6e4fdffd945f6845db7fc49e81 Mon Sep 17 00:00:00 2001 From: Florian Duros Date: Mon, 27 Jan 2025 18:10:02 +0100 Subject: [PATCH] test(legacy crypto): update existing integ tests to run successfully --- spec/integ/crypto/crypto.spec.ts | 441 +------------------ spec/integ/crypto/device-dehydration.spec.ts | 4 +- spec/integ/crypto/megolm-backup.spec.ts | 114 +---- spec/integ/crypto/rust-crypto.spec.ts | 3 +- spec/integ/crypto/verification.spec.ts | 3 +- 5 files changed, 21 insertions(+), 544 deletions(-) diff --git a/spec/integ/crypto/crypto.spec.ts b/spec/integ/crypto/crypto.spec.ts index 466a46e39a7..6b9fc35679f 100644 --- a/spec/integ/crypto/crypto.spec.ts +++ b/spec/integ/crypto/crypto.spec.ts @@ -24,7 +24,6 @@ import Olm from "@matrix-org/olm"; import * as testUtils from "../../test-utils/test-utils"; import { - advanceTimersUntil, CRYPTO_BACKENDS, emitPromise, getSyncResponse, @@ -49,7 +48,6 @@ import { Category, ClientEvent, createClient, - CryptoEvent, HistoryVisibility, IClaimOTKsResult, IContent, @@ -65,7 +63,6 @@ import { RoomMember, RoomStateEvent, } from "../../../src/matrix"; -import { DeviceInfo } from "../../../src/crypto/deviceinfo"; import { E2EKeyReceiver } from "../../test-utils/E2EKeyReceiver"; import { ISyncResponder, SyncResponder } from "../../test-utils/SyncResponder"; import { defer, escapeRegExp } from "../../../src/utils"; @@ -103,6 +100,7 @@ import { AccountDataAccumulator } from "../../test-utils/AccountDataAccumulator" import { UNSIGNED_MEMBERSHIP_FIELD } from "../../../src/@types/event"; import { KnownMembership } from "../../../src/@types/membership"; import { KeyBackup } from "../../../src/rust-crypto/backup.ts"; +import { CryptoEvent } from "../../../src/crypto-api"; afterEach(() => { // reset fake-indexeddb after each test, to make sure we don't leak connections @@ -415,13 +413,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); await startClientAndAwaitFirstSync(); - // if we're using the old crypto impl, stub out some methods in the device manager. - // TODO: replace this with intercepts of the /keys/query endpoint to make it impl agnostic. - if (aliceClient.crypto) { - aliceClient.crypto.deviceList.downloadKeys = () => Promise.resolve(new Map()); - aliceClient.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; - } - const p2pSession = await createOlmSession(testOlmAccount, keyReceiver); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); @@ -878,13 +869,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); await startClientAndAwaitFirstSync(); - // if we're using the old crypto impl, stub out some methods in the device manager. - // TODO: replace this with intercepts of the /keys/query endpoint to make it impl agnostic. - if (aliceClient.crypto) { - aliceClient.crypto.deviceList.downloadKeys = () => Promise.resolve(new Map()); - aliceClient.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; - } - const p2pSession = await createOlmSession(testOlmAccount, keyReceiver); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); @@ -939,13 +923,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); await startClientAndAwaitFirstSync(); - // if we're using the old crypto impl, stub out some methods in the device manager. - // TODO: replace this with intercepts of the /keys/query endpoint to make it impl agnostic. - if (aliceClient.crypto) { - aliceClient.crypto.deviceList.downloadKeys = () => Promise.resolve(new Map()); - aliceClient.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; - } - const p2pSession = await createOlmSession(testOlmAccount, keyReceiver); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); @@ -1030,13 +1007,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, // fire off the prepare request const room = aliceClient.getRoom(ROOM_ID); expect(room).toBeTruthy(); - const p = aliceClient.prepareToEncrypt(room!); + aliceClient.getCrypto()?.prepareToEncrypt(room!); // we expect to get a room key message await expectSendRoomKey("@bob:xyz", testOlmAccount); - - // the prepare request should complete successfully. - await p; }); it("Alice sends a megolm message with GlobalErrorOnUnknownDevices=false", async () => { @@ -1095,7 +1069,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, ]); // Finally the interesting part: discard the session. - aliceClient.forceDiscardSession(ROOM_ID); + aliceClient.getCrypto()!.forceDiscardSession(ROOM_ID); // Now when we send the next message, we should get a *new* megolm session. const inboundGroupSessionPromise2 = expectSendRoomKey("@bob:xyz", testOlmAccount); @@ -1103,74 +1077,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, await Promise.all([aliceClient.sendTextMessage(ROOM_ID, "test2"), p2]); }); - oldBackendOnly("Alice sends a megolm message", async () => { - // TODO: do something about this for the rust backend. - // Currently it fails because we don't respect the default GlobalErrorOnUnknownDevices and - // send messages to unknown devices. - - expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); - await startClientAndAwaitFirstSync(); - const p2pSession = await establishOlmSession(aliceClient, keyReceiver, syncResponder, testOlmAccount); - - syncResponder.sendOrQueueSyncResponse(getSyncResponse(["@bob:xyz"])); - await syncPromise(aliceClient); - - // start out with the device unknown - the send should be rejected. - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - - await aliceClient.sendTextMessage(ROOM_ID, "test").then( - () => { - throw new Error("sendTextMessage failed on an unknown device"); - }, - (e) => { - expect(e.name).toEqual("UnknownDeviceError"); - }, - ); - - // mark the device as known, and resend. - aliceClient.setDeviceKnown("@bob:xyz", "DEVICE_ID"); - - const room = aliceClient.getRoom(ROOM_ID)!; - const pendingMsg = room.getPendingEvents()[0]; - - const inboundGroupSessionPromise = expectSendRoomKey("@bob:xyz", testOlmAccount, p2pSession); - - await Promise.all([ - aliceClient.resendEvent(pendingMsg, room), - expectSendMegolmMessage(inboundGroupSessionPromise), - ]); - }); - - oldBackendOnly("We shouldn't attempt to send to blocked devices", async () => { - expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); - await startClientAndAwaitFirstSync(); - await establishOlmSession(aliceClient, keyReceiver, syncResponder, testOlmAccount); - - syncResponder.sendOrQueueSyncResponse(getSyncResponse(["@bob:xyz"])); - await syncPromise(aliceClient); - - logger.log("Forcing alice to download our device keys"); - - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - - await aliceClient.downloadKeys(["@bob:xyz"]); - - logger.log("Telling alice to block our device"); - aliceClient.setDeviceBlocked("@bob:xyz", "DEVICE_ID"); - - logger.log("Telling alice to send a megolm message"); - fetchMock.putOnce({ url: new RegExp("/send/"), name: "send-event" }, { event_id: "$event_id" }); - fetchMock.putOnce({ url: new RegExp("/sendToDevice/m.room_key.withheld/"), name: "send-withheld" }, {}); - - await aliceClient.sendTextMessage(ROOM_ID, "test"); - - // check that the event and withheld notifications were both sent - expect(fetchMock.done("send-event")).toBeTruthy(); - expect(fetchMock.done("send-withheld")).toBeTruthy(); - }); - describe("get|setGlobalErrorOnUnknownDevices", () => { it("should raise an error if crypto is disabled", () => { aliceClient["cryptoBackend"] = undefined; @@ -1218,58 +1124,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, }); describe("get|setGlobalBlacklistUnverifiedDevices", () => { - it("should raise an error if crypto is disabled", () => { - aliceClient["cryptoBackend"] = undefined; - expect(() => aliceClient.setGlobalBlacklistUnverifiedDevices(true)).toThrow("encryption disabled"); - expect(() => aliceClient.getGlobalBlacklistUnverifiedDevices()).toThrow("encryption disabled"); - }); - - oldBackendOnly("should disable sending to unverified devices", async () => { - expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); - await startClientAndAwaitFirstSync(); - const p2pSession = await establishOlmSession(aliceClient, keyReceiver, syncResponder, testOlmAccount); - - // tell alice we share a room with bob - syncResponder.sendOrQueueSyncResponse(getSyncResponse(["@bob:xyz"])); - await syncPromise(aliceClient); - - logger.log("Forcing alice to download our device keys"); - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - - await aliceClient.downloadKeys(["@bob:xyz"]); - - logger.log("Telling alice to block messages to unverified devices"); - expect(aliceClient.getGlobalBlacklistUnverifiedDevices()).toBeFalsy(); - aliceClient.setGlobalBlacklistUnverifiedDevices(true); - expect(aliceClient.getGlobalBlacklistUnverifiedDevices()).toBeTruthy(); - - logger.log("Telling alice to send a megolm message"); - fetchMock.putOnce(new RegExp("/send/"), { event_id: "$event_id" }); - fetchMock.putOnce(new RegExp("/sendToDevice/m.room_key.withheld/"), {}); - - await aliceClient.sendTextMessage(ROOM_ID, "test"); - - // Now, let's mark the device as verified, and check that keys are sent to it. - - logger.log("Marking the device as verified"); - // XXX: this is an integration test; we really ought to do this via the cross-signing dance - const d = aliceClient.crypto!.deviceList.getStoredDevice("@bob:xyz", "DEVICE_ID")!; - d.verified = DeviceInfo.DeviceVerification.VERIFIED; - aliceClient.crypto?.deviceList.storeDevicesForUser("@bob:xyz", { DEVICE_ID: d }); - - const inboundGroupSessionPromise = expectSendRoomKey("@bob:xyz", testOlmAccount, p2pSession); - - logger.log("Asking alice to re-send"); - await Promise.all([ - expectSendMegolmMessage(inboundGroupSessionPromise).then((decrypted) => { - expect(decrypted.type).toEqual("m.room.message"); - expect(decrypted.content!.body).toEqual("test"); - }), - aliceClient.sendTextMessage(ROOM_ID, "test"), - ]); - }); - it("should send a m.unverified code in toDevice messages to an unverified device when globalBlacklistUnverifiedDevices=true", async () => { aliceClient.getCrypto()!.globalBlacklistUnverifiedDevices = true; @@ -1472,272 +1326,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expect(sessionId).not.toEqual(newSessionId); }); - oldBackendOnly("We should start a new megolm session when a device is blocked", async () => { - expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); - await startClientAndAwaitFirstSync(); - const p2pSession = await establishOlmSession(aliceClient, keyReceiver, syncResponder, testOlmAccount); - - syncResponder.sendOrQueueSyncResponse(getSyncResponse(["@bob:xyz"])); - await syncPromise(aliceClient); - - logger.log("Fetching bob's devices and marking known"); - - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - - await aliceClient.downloadKeys(["@bob:xyz"]); - await aliceClient.setDeviceKnown("@bob:xyz", "DEVICE_ID"); - - logger.log("Telling alice to send a megolm message"); - - let megolmSessionId: string; - const inboundGroupSessionPromise = expectSendRoomKey("@bob:xyz", testOlmAccount, p2pSession); - inboundGroupSessionPromise.then((igs) => { - megolmSessionId = igs.session_id(); - }); - - await Promise.all([ - aliceClient.sendTextMessage(ROOM_ID, "test"), - expectSendMegolmMessage(inboundGroupSessionPromise), - ]); - - logger.log("Telling alice to block our device"); - aliceClient.setDeviceBlocked("@bob:xyz", "DEVICE_ID"); - - logger.log("Telling alice to send another megolm message"); - - fetchMock.putOnce( - { url: new RegExp("/send/"), name: "send-event" }, - (url: string, opts: RequestInit): FetchMock.MockResponse => { - const content = JSON.parse(opts.body as string); - logger.log("/send:", content); - // make sure that a new session is used - expect(content.session_id).not.toEqual(megolmSessionId); - return { - event_id: "$event_id", - }; - }, - ); - fetchMock.putOnce({ url: new RegExp("/sendToDevice/m.room_key.withheld/"), name: "send-withheld" }, {}); - - await aliceClient.sendTextMessage(ROOM_ID, "test2"); - - // check that the event and withheld notifications were both sent - expect(fetchMock.done("send-event")).toBeTruthy(); - expect(fetchMock.done("send-withheld")).toBeTruthy(); - }); - - // https://github.com/vector-im/element-web/issues/2676 - oldBackendOnly("Alice should send to her other devices", async () => { - // for this test, we make the testOlmAccount be another of Alice's devices. - // it ought to get included in messages Alice sends. - expectAliceKeyQuery(getTestKeysQueryResponse(aliceClient.getUserId()!)); - - await startClientAndAwaitFirstSync(); - // an encrypted room with just alice - const syncResponse = { - next_batch: 1, - rooms: { - join: { - [ROOM_ID]: { - state: { - events: [ - testUtils.mkEvent({ - type: "m.room.encryption", - skey: "", - content: { algorithm: "m.megolm.v1.aes-sha2" }, - }), - testUtils.mkMembership({ - mship: KnownMembership.Join, - sender: aliceClient.getUserId()!, - }), - ], - }, - }, - }, - }, - }; - syncResponder.sendOrQueueSyncResponse(syncResponse); - - await syncPromise(aliceClient); - - // start out with the device unknown - the send should be rejected. - try { - await aliceClient.sendTextMessage(ROOM_ID, "test"); - throw new Error("sendTextMessage succeeded on an unknown device"); - } catch (e) { - expect((e as any).name).toEqual("UnknownDeviceError"); - expect([...(e as any).devices.keys()]).toEqual([aliceClient.getUserId()!]); - expect((e as any).devices.get(aliceClient.getUserId()!).has("DEVICE_ID")).toBeTruthy(); - } - - // mark the device as known, and resend. - aliceClient.setDeviceKnown(aliceClient.getUserId()!, "DEVICE_ID"); - expectAliceKeyClaim((url: string, opts: RequestInit): FetchMock.MockResponse => { - const content = JSON.parse(opts.body as string); - expect(content.one_time_keys[aliceClient.getUserId()!].DEVICE_ID).toEqual("signed_curve25519"); - return getTestKeysClaimResponse(aliceClient.getUserId()!); - }); - - const inboundGroupSessionPromise = expectSendRoomKey(aliceClient.getUserId()!, testOlmAccount); - - let decrypted: Partial = {}; - - // Grab the event that we'll need to resend - const room = aliceClient.getRoom(ROOM_ID)!; - const pendingEvents = room.getPendingEvents(); - expect(pendingEvents.length).toEqual(1); - const unsentEvent = pendingEvents[0]; - - await Promise.all([ - expectSendMegolmMessage(inboundGroupSessionPromise).then((d) => { - decrypted = d; - }), - aliceClient.resendEvent(unsentEvent, room), - ]); - - expect(decrypted.type).toEqual("m.room.message"); - expect(decrypted.content?.body).toEqual("test"); - }); - - oldBackendOnly("Alice should wait for device list to complete when sending a megolm message", async () => { - expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); - await startClientAndAwaitFirstSync(); - await establishOlmSession(aliceClient, keyReceiver, syncResponder, testOlmAccount); - - syncResponder.sendOrQueueSyncResponse(getSyncResponse(["@bob:xyz"])); - await syncPromise(aliceClient); - - // this will block - logger.log("Forcing alice to download our device keys"); - const downloadPromise = aliceClient.downloadKeys(["@bob:xyz"]); - - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - - // so will this. - const sendPromise = aliceClient.sendTextMessage(ROOM_ID, "test").then( - () => { - throw new Error("sendTextMessage failed on an unknown device"); - }, - (e) => { - expect(e.name).toEqual("UnknownDeviceError"); - }, - ); - - expectAliceKeyQuery(getTestKeysQueryResponse("@bob:xyz")); - - await Promise.all([downloadPromise, sendPromise]); - }); - - oldBackendOnly("Alice exports megolm keys and imports them to a new device", async () => { - expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); - await startClientAndAwaitFirstSync(); - - // if we're using the old crypto impl, stub out some methods in the device manager. - // TODO: replace this with intercepts of the /keys/query endpoint to make it impl agnostic. - if (aliceClient.crypto) { - aliceClient.crypto.deviceList.downloadKeys = () => Promise.resolve(new Map()); - aliceClient.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; - } - - // establish an olm session with alice - const p2pSession = await createOlmSession(testOlmAccount, keyReceiver); - - const groupSession = new Olm.OutboundGroupSession(); - groupSession.create(); - - // make the room_key event - const roomKeyEncrypted = encryptGroupSessionKey({ - recipient: aliceClient.getUserId()!, - recipientCurve25519Key: keyReceiver.getDeviceKey(), - recipientEd25519Key: keyReceiver.getSigningKey(), - olmAccount: testOlmAccount, - p2pSession: p2pSession, - groupSession: groupSession, - room_id: ROOM_ID, - }); - - // encrypt a message with the group session - const messageEncrypted = encryptMegolmEvent({ - senderKey: testSenderKey, - groupSession: groupSession, - room_id: ROOM_ID, - }); - - // Alice gets both the events in a single sync - syncResponder.sendOrQueueSyncResponse({ - next_batch: 1, - to_device: { - events: [roomKeyEncrypted], - }, - rooms: { - join: { [ROOM_ID]: { timeline: { events: [messageEncrypted] } } }, - }, - }); - await syncPromise(aliceClient); - - const room = aliceClient.getRoom(ROOM_ID)!; - await room.decryptCriticalEvents(); - - // it probably won't be decrypted yet, because it takes a while to process the olm keys - const decryptedEvent = await testUtils.awaitDecryption(room.getLiveTimeline().getEvents()[0], { - waitOnDecryptionFailure: true, - }); - expect(decryptedEvent.getContent().body).toEqual("42"); - - const exported = await aliceClient.getCrypto()!.exportRoomKeysAsJson(); - - // start a new client - await aliceClient.stopClient(); - - const homeserverUrl = "https://alice-server2.com"; - aliceClient = createClient({ - baseUrl: homeserverUrl, - userId: "@alice:localhost", - accessToken: "akjgkrgjs", - deviceId: "xzcvb", - }); - - keyReceiver = new E2EKeyReceiver(homeserverUrl); - syncResponder = new SyncResponder(homeserverUrl); - await initCrypto(aliceClient); - await aliceClient.getCrypto()!.importRoomKeysAsJson(exported); - expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); - await startClientAndAwaitFirstSync(); - - aliceClient.startClient(); - - // if we're using the old crypto impl, stub out some methods in the device manager. - // TODO: replace this with intercepts of the /keys/query endpoint to make it impl agnostic. - if (aliceClient.crypto) { - aliceClient.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; - } - - const syncResponse = { - next_batch: 1, - rooms: { - join: { [ROOM_ID]: { timeline: { events: [messageEncrypted] } } }, - }, - }; - - syncResponder.sendOrQueueSyncResponse(syncResponse); - await syncPromise(aliceClient); - - const event = room.getLiveTimeline().getEvents()[0]; - expect(event.getContent().body).toEqual("42"); - }); - it("Alice can decrypt a message with falsey content", async () => { expectAliceKeyQuery({ device_keys: { "@alice:localhost": {} }, failures: {} }); await startClientAndAwaitFirstSync(); - // if we're using the old crypto impl, stub out some methods in the device manager. - // TODO: replace this with intercepts of the /keys/query endpoint to make it impl agnostic. - if (aliceClient.crypto) { - aliceClient.crypto.deviceList.downloadKeys = () => Promise.resolve(new Map()); - aliceClient.crypto.deviceList.getUserByIdentityKey = () => "@bob:xyz"; - } - const p2pSession = await createOlmSession(testOlmAccount, keyReceiver); const groupSession = new Olm.OutboundGroupSession(); groupSession.create(); @@ -2790,6 +2382,10 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, await bootstrapSecurity(backupVersion); const check = await aliceClient.getCrypto()!.checkKeyBackupAndEnable(); + fetchMock.get( + `path:/_matrix/client/v3/room_keys/version/${check!.backupInfo.version}`, + check!.backupInfo!, + ); // Import a new key that should be uploaded const newKey = testData.MEGOLM_SESSION_DATA; @@ -2824,9 +2420,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, fetchMock.get("express:/_matrix/client/v3/room_keys/keys", keyBackupData); // should be able to restore from 4S - const importResult = await advanceTimersUntil( - aliceClient.restoreKeyBackupWithSecretStorage(check!.backupInfo!), - ); + await aliceClient.getCrypto()!.loadSessionBackupPrivateKeyFromSecretStorage(); + const importResult = await aliceClient.getCrypto()!.restoreKeyBackup(); expect(importResult.imported).toStrictEqual(1); }); @@ -2891,19 +2486,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, const newBackupUploadPromise = awaitMegolmBackupKeyUpload(); - // Track calls to scheduleAllGroupSessionsForBackup. This is - // only relevant on legacy encryption. - const scheduleAllGroupSessionsForBackup = jest.fn(); - if (backend === "libolm") { - aliceClient.crypto!.backupManager.scheduleAllGroupSessionsForBackup = - scheduleAllGroupSessionsForBackup; - } else { - // With Rust crypto, we don't need to call this function, so - // we call the dummy value here so we pass our later - // expectation. - scheduleAllGroupSessionsForBackup(); - } - await aliceClient.getCrypto()!.resetKeyBackup(); await awaitDeleteCalled; await newBackupStatusUpdate; @@ -2915,11 +2497,8 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("crypto (%s)", (backend: string, expect(nextVersion).toBeDefined(); expect(nextVersion).not.toEqual(currentVersion); expect(nextKey).not.toEqual(currentBackupKey); - expect(scheduleAllGroupSessionsForBackup).toHaveBeenCalled(); - // The `deleteKeyBackupVersion` API is deprecated but has been modified to work with both crypto backend - // ensure that it works anyhow - await aliceClient.deleteKeyBackupVersion(nextVersion!); + await aliceClient.getCrypto()!.deleteKeyBackupVersion(nextVersion!); await aliceClient.getCrypto()!.checkKeyBackupAndEnable(); // XXX Legacy crypto does not update 4S when doing that; should ensure that rust implem does it. expect(await aliceClient.getCrypto()!.getActiveSessionBackupVersion()).toBeNull(); diff --git a/spec/integ/crypto/device-dehydration.spec.ts b/spec/integ/crypto/device-dehydration.spec.ts index cf319a9878c..4a1165128e1 100644 --- a/spec/integ/crypto/device-dehydration.spec.ts +++ b/spec/integ/crypto/device-dehydration.spec.ts @@ -172,8 +172,8 @@ async function initializeSecretStorage( privateKey: new Uint8Array(32), }; } - await matrixClient.bootstrapCrossSigning({ setupNewCrossSigning: true }); - await matrixClient.bootstrapSecretStorage({ + await matrixClient.getCrypto()!.bootstrapCrossSigning({ setupNewCrossSigning: true }); + await matrixClient.getCrypto()!.bootstrapSecretStorage({ createSecretStorageKey, setupNewSecretStorage: true, setupNewKeyBackup: false, diff --git a/spec/integ/crypto/megolm-backup.spec.ts b/spec/integ/crypto/megolm-backup.spec.ts index eff0ff567e1..b3d6fe335a8 100644 --- a/spec/integ/crypto/megolm-backup.spec.ts +++ b/spec/integ/crypto/megolm-backup.spec.ts @@ -117,7 +117,6 @@ function mockUploadEmitter( 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. - const oldBackendOnly = backend === "rust-sdk" ? test.skip : test; const newBackendOnly = backend === "libolm" ? test.skip : test; const isNewBackend = backend === "rust-sdk"; @@ -344,43 +343,14 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe fetchMock.get("express:/_matrix/client/v3/room_keys/keys", fullBackup); const check = await aliceCrypto.checkKeyBackupAndEnable(); - - let onKeyCached: () => void; - const awaitKeyCached = new Promise((resolve) => { - onKeyCached = resolve; - }); - await aliceCrypto.storeSessionBackupPrivateKey( decodeRecoveryKey(testData.BACKUP_DECRYPTION_KEY_BASE58), check!.backupInfo!.version!, ); - const result = await advanceTimersUntil( - isNewBackend - ? aliceCrypto.restoreKeyBackup() - : aliceClient.restoreKeyBackupWithRecoveryKey( - testData.BACKUP_DECRYPTION_KEY_BASE58, - undefined, - undefined, - check!.backupInfo!, - { - cacheCompleteCallback: () => onKeyCached(), - }, - ), - ); + const result = await advanceTimersUntil(aliceCrypto.restoreKeyBackup()); expect(result.imported).toStrictEqual(1); - - if (isNewBackend) return; - - await awaitKeyCached; - - // The key should be now cached - const afterCache = await advanceTimersUntil( - aliceClient.restoreKeyBackupWithCache(undefined, undefined, check!.backupInfo!), - ); - - expect(afterCache.imported).toStrictEqual(1); }); /** @@ -434,19 +404,9 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe ); const progressCallback = jest.fn(); - const result = await (isNewBackend - ? aliceCrypto.restoreKeyBackup({ - progressCallback, - }) - : aliceClient.restoreKeyBackupWithRecoveryKey( - testData.BACKUP_DECRYPTION_KEY_BASE58, - undefined, - undefined, - check!.backupInfo!, - { - progressCallback, - }, - )); + const result = await aliceCrypto.restoreKeyBackup({ + progressCallback, + }); expect(result.imported).toStrictEqual(expectedTotal); // Should be called 5 times: 200*4 plus one chunk with the remaining 32 @@ -508,17 +468,7 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe ); const progressCallback = jest.fn(); - const result = await (isNewBackend - ? aliceCrypto.restoreKeyBackup({ progressCallback }) - : aliceClient.restoreKeyBackupWithRecoveryKey( - testData.BACKUP_DECRYPTION_KEY_BASE58, - undefined, - undefined, - check!.backupInfo!, - { - progressCallback, - }, - )); + const result = await aliceCrypto.restoreKeyBackup({ progressCallback }); expect(result.total).toStrictEqual(expectedTotal); // A chunk failed to import @@ -574,40 +524,13 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe check!.backupInfo!.version!, ); - const result = await (isNewBackend - ? aliceCrypto.restoreKeyBackup() - : aliceClient.restoreKeyBackupWithRecoveryKey( - testData.BACKUP_DECRYPTION_KEY_BASE58, - undefined, - undefined, - check!.backupInfo!, - )); + const result = await aliceCrypto.restoreKeyBackup(); expect(result.total).toStrictEqual(expectedTotal); // A chunk failed to import expect(result.imported).toStrictEqual(expectedTotal - decryptionFailureCount); }); - oldBackendOnly("recover specific session from backup", async function () { - fetchMock.get( - "express:/_matrix/client/v3/room_keys/keys/:room_id/:session_id", - testData.CURVE25519_KEY_BACKUP_DATA, - ); - - const check = await aliceCrypto.checkKeyBackupAndEnable(); - - const result = await advanceTimersUntil( - aliceClient.restoreKeyBackupWithRecoveryKey( - testData.BACKUP_DECRYPTION_KEY_BASE58, - ROOM_ID, - testData.MEGOLM_SESSION_DATA.session_id, - check!.backupInfo!, - ), - ); - - expect(result.imported).toStrictEqual(1); - }); - newBackendOnly( "Should get the decryption key from the secret storage and restore the key backup", async function () { @@ -634,31 +557,6 @@ describe.each(Object.entries(CRYPTO_BACKENDS))("megolm-keys backup (%s)", (backe }, ); - oldBackendOnly("Fails on bad recovery key", async function () { - const fullBackup = { - rooms: { - [ROOM_ID]: { - sessions: { - [testData.MEGOLM_SESSION_DATA.session_id]: testData.CURVE25519_KEY_BACKUP_DATA, - }, - }, - }, - }; - - fetchMock.get("express:/_matrix/client/v3/room_keys/keys", fullBackup); - - const check = await aliceCrypto.checkKeyBackupAndEnable(); - - await expect( - aliceClient.restoreKeyBackupWithRecoveryKey( - "EsTx A7Xn aNFF k3jH zpV3 MQoN LJEg mscC HecF 982L wC77 mYQD", - undefined, - undefined, - check!.backupInfo!, - ), - ).rejects.toThrow(); - }); - newBackendOnly("Should throw an error if the decryption key is not found in cache", async () => { await expect(aliceCrypto.restoreKeyBackup()).rejects.toThrow("No decryption key found in crypto store"); }); diff --git a/spec/integ/crypto/rust-crypto.spec.ts b/spec/integ/crypto/rust-crypto.spec.ts index 5aee7e83582..2394d9a3ef9 100644 --- a/spec/integ/crypto/rust-crypto.spec.ts +++ b/spec/integ/crypto/rust-crypto.spec.ts @@ -18,12 +18,13 @@ import "fake-indexeddb/auto"; import { IDBFactory } from "fake-indexeddb"; import fetchMock from "fetch-mock-jest"; -import { createClient, CryptoEvent, IndexedDBCryptoStore } from "../../../src"; +import { createClient, IndexedDBCryptoStore } from "../../../src"; import { populateStore } from "../../test-utils/test_indexeddb_cryptostore_dump"; import { MSK_NOT_CACHED_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/no_cached_msk_dump"; import { IDENTITY_NOT_TRUSTED_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/unverified"; import { FULL_ACCOUNT_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/full_account"; import { EMPTY_ACCOUNT_DATASET } from "../../test-utils/test_indexeddb_cryptostore_dump/empty_account"; +import { CryptoEvent } from "../../../src/crypto-api"; jest.setTimeout(15000); diff --git a/spec/integ/crypto/verification.spec.ts b/spec/integ/crypto/verification.spec.ts index a4cee9e8365..bc7a976bc0f 100644 --- a/spec/integ/crypto/verification.spec.ts +++ b/spec/integ/crypto/verification.spec.ts @@ -25,7 +25,6 @@ import Olm from "@matrix-org/olm"; import { createClient, - CryptoEvent, DeviceVerification, IContent, ICreateClientOpts, @@ -81,7 +80,7 @@ import { getTestOlmAccountKeys, ToDeviceEvent, } from "./olm-utils"; -import { KeyBackupInfo } from "../../../src/crypto-api"; +import { KeyBackupInfo, CryptoEvent } from "../../../src/crypto-api"; import { encodeBase64 } from "../../../src/base64"; // The verification flows use javascript timers to set timeouts. We tell jest to use mock timer implementations