diff --git a/spec/unit/crypto/DeviceList.spec.js b/spec/unit/crypto/DeviceList.spec.ts similarity index 89% rename from spec/unit/crypto/DeviceList.spec.js rename to spec/unit/crypto/DeviceList.spec.ts index bb113dccf..cb7f0fb0f 100644 --- a/spec/unit/crypto/DeviceList.spec.js +++ b/spec/unit/crypto/DeviceList.spec.ts @@ -1,7 +1,7 @@ /* Copyright 2017 Vector Creations Ltd Copyright 2018, 2019 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,8 +20,10 @@ import { logger } from "../../../src/logger"; import * as utils from "../../../src/utils"; import { MemoryCryptoStore } from "../../../src/crypto/store/memory-crypto-store"; import { DeviceList } from "../../../src/crypto/DeviceList"; +import { IDownloadKeyResult, MatrixClient } from "../../../src"; +import { OlmDevice } from "../../../src/crypto/OlmDevice"; -const signedDeviceList = { +const signedDeviceList: IDownloadKeyResult = { "failures": {}, "device_keys": { "@test1:sw1v.org": { @@ -45,13 +47,15 @@ const signedDeviceList = { "m.megolm.v1.aes-sha2", ], "device_id": "HGKAWHRVJQ", - "unsigned": {}, + "unsigned": { + "device_display_name": "", + }, }, }, }, }; -const signedDeviceList2 = { +const signedDeviceList2: IDownloadKeyResult = { "failures": {}, "device_keys": { "@test2:sw1v.org": { @@ -75,7 +79,9 @@ const signedDeviceList2 = { "m.megolm.v1.aes-sha2", ], "device_id": "QJVRHWAKGH", - "unsigned": {}, + "unsigned": { + "device_display_name": "", + }, }, }, }, @@ -104,10 +110,10 @@ describe('DeviceList', function() { downloadKeysForUsers: downloadSpy, getUserId: () => '@test1:sw1v.org', deviceId: 'HGKAWHRVJQ', - }; + } as unknown as MatrixClient; const mockOlm = { verifySignature: function(key, message, signature) {}, - }; + } as unknown as OlmDevice; const dl = new DeviceList(baseApis, cryptoStore, mockOlm, keyDownloadChunkSize); deviceLists.push(dl); return dl; @@ -118,7 +124,7 @@ describe('DeviceList', function() { dl.startTrackingDeviceList('@test1:sw1v.org'); - const queryDefer1 = utils.defer(); + const queryDefer1 = utils.defer(); downloadSpy.mockReturnValue(queryDefer1.promise); const prom1 = dl.refreshOutdatedDeviceLists(); @@ -138,7 +144,7 @@ describe('DeviceList', function() { dl.startTrackingDeviceList('@test1:sw1v.org'); - const queryDefer1 = utils.defer(); + const queryDefer1 = utils.defer(); downloadSpy.mockReturnValue(queryDefer1.promise); const prom1 = dl.refreshOutdatedDeviceLists(); @@ -155,6 +161,7 @@ describe('DeviceList', function() { dl.saveIfDirty().then(() => { // the first request completes queryDefer1.resolve({ + failures: {}, device_keys: { '@test1:sw1v.org': {}, }, @@ -166,7 +173,7 @@ describe('DeviceList', function() { logger.log("Creating new devicelist to simulate app reload"); downloadSpy.mockReset(); const dl2 = createTestDeviceList(); - const queryDefer3 = utils.defer(); + const queryDefer3 = utils.defer(); downloadSpy.mockReturnValue(queryDefer3.promise); const prom3 = dl2.refreshOutdatedDeviceLists(); @@ -190,9 +197,9 @@ describe('DeviceList', function() { dl.startTrackingDeviceList('@test1:sw1v.org'); dl.startTrackingDeviceList('@test2:sw1v.org'); - const queryDefer1 = utils.defer(); + const queryDefer1 = utils.defer(); downloadSpy.mockReturnValueOnce(queryDefer1.promise); - const queryDefer2 = utils.defer(); + const queryDefer2 = utils.defer(); downloadSpy.mockReturnValueOnce(queryDefer2.promise); const prom1 = dl.refreshOutdatedDeviceLists(); diff --git a/spec/unit/crypto/outgoing-room-key-requests.spec.js b/spec/unit/crypto/outgoing-room-key-requests.spec.ts similarity index 73% rename from spec/unit/crypto/outgoing-room-key-requests.spec.js rename to spec/unit/crypto/outgoing-room-key-requests.spec.ts index 4a18e1765..c572a63eb 100644 --- a/spec/unit/crypto/outgoing-room-key-requests.spec.js +++ b/spec/unit/crypto/outgoing-room-key-requests.spec.ts @@ -43,13 +43,15 @@ const requests = [ describe.each([ ["IndexedDBCryptoStore", - () => new IndexedDBCryptoStore(global.indexedDB, "tests")], + () => new IndexedDBCryptoStore(global.indexedDB, "tests")], ["LocalStorageCryptoStore", - () => new IndexedDBCryptoStore(undefined, "tests")], + () => new IndexedDBCryptoStore(undefined, "tests")], ["MemoryCryptoStore", () => { const store = new IndexedDBCryptoStore(undefined, "tests"); - store._backend = new MemoryCryptoStore(); - store._backendPromise = Promise.resolve(store._backend); + // @ts-ignore set private properties + store.backend = new MemoryCryptoStore(); + // @ts-ignore + store.backendPromise = Promise.resolve(store.backend); return store; }], ])("Outgoing room key requests [%s]", function(name, dbFactory) { @@ -64,22 +66,22 @@ describe.each([ }); it("getAllOutgoingRoomKeyRequestsByState retrieves all entries in a given state", - async () => { - const r = await + async () => { + const r = await store.getAllOutgoingRoomKeyRequestsByState(RoomKeyRequestState.Sent); - expect(r).toHaveLength(2); - requests.filter((e) => e.state === RoomKeyRequestState.Sent).forEach((e) => { - expect(r).toContainEqual(e); + expect(r).toHaveLength(2); + requests.filter((e) => e.state === RoomKeyRequestState.Sent).forEach((e) => { + expect(r).toContainEqual(e); + }); }); - }); test("getOutgoingRoomKeyRequestByState retrieves any entry in a given state", - async () => { - const r = + async () => { + const r = await store.getOutgoingRoomKeyRequestByState([RoomKeyRequestState.Sent]); - expect(r).not.toBeNull(); - expect(r).not.toBeUndefined(); - expect(r.state).toEqual(RoomKeyRequestState.Sent); - expect(requests).toContainEqual(r); - }); + expect(r).not.toBeNull(); + expect(r).not.toBeUndefined(); + expect(r.state).toEqual(RoomKeyRequestState.Sent); + expect(requests).toContainEqual(r); + }); }); diff --git a/spec/unit/crypto/secrets.spec.js b/spec/unit/crypto/secrets.spec.ts similarity index 91% rename from spec/unit/crypto/secrets.spec.js rename to spec/unit/crypto/secrets.spec.ts index 6a1e41f40..1ed9c21e2 100644 --- a/spec/unit/crypto/secrets.spec.js +++ b/spec/unit/crypto/secrets.spec.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 2022 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -24,15 +24,18 @@ import { encryptAES } from "../../../src/crypto/aes"; import { resetCrossSigningKeys, createSecretStorageKey } from "./crypto-utils"; import { logger } from '../../../src/logger'; import * as utils from "../../../src/utils"; +import { ICreateClientOpts } from '../../../src'; +import { ISecretStorageKeyInfo } from '../../../src/crypto/api'; try { + // eslint-disable-next-line @typescript-eslint/no-var-requires const crypto = require('crypto'); utils.setCrypto(crypto); } catch (err) { logger.log('nodejs was compiled without crypto support'); } -async function makeTestClient(userInfo, options) { +async function makeTestClient(userInfo: { userId: string, deviceId: string}, options: Partial = {}) { const client = (new TestClient( userInfo.userId, userInfo.deviceId, undefined, undefined, options, )).client; @@ -46,7 +49,7 @@ async function makeTestClient(userInfo, options) { await client.initCrypto(); // No need to download keys for these tests - client.crypto.downloadKeys = async function() {}; + jest.spyOn(client.crypto, 'downloadKeys').mockResolvedValue({}); return client; } @@ -54,7 +57,7 @@ async function makeTestClient(userInfo, options) { // Wrapper around pkSign to return a signed object. pkSign returns the // signature, rather than the signed object. function sign(obj, key, userId) { - olmlib.pkSign(obj, key, userId); + olmlib.pkSign(obj, key, userId, ''); return obj; } @@ -84,7 +87,7 @@ describe("Secrets", function() { }, }; - const getKey = jest.fn(e => { + const getKey = jest.fn().mockImplementation(async e => { expect(Object.keys(e.keys)).toEqual(["abc"]); return ['abc', key]; }); @@ -93,7 +96,7 @@ describe("Secrets", function() { { userId: "@alice:example.com", deviceId: "Osborne2" }, { cryptoCallbacks: { - getCrossSigningKey: t => signingKey, + getCrossSigningKey: async t => signingKey, getSecretStorageKey: getKey, }, }, @@ -104,17 +107,19 @@ describe("Secrets", function() { const secretStorage = alice.crypto.secretStorage; - alice.setAccountData = async function(eventType, contents, callback) { - alice.store.storeAccountDataEvents([ - new MatrixEvent({ - type: eventType, - content: contents, - }), - ]); - if (callback) { - callback(); - } - }; + jest.spyOn(alice, 'setAccountData').mockImplementation( + async function(eventType, contents, callback) { + alice.store.storeAccountDataEvents([ + new MatrixEvent({ + type: eventType, + content: contents, + }), + ]); + if (callback) { + callback(undefined, undefined); + } + return {}; + }); const keyAccountData = { algorithm: SECRET_STORAGE_ALGORITHM_V1_AES, @@ -170,7 +175,7 @@ describe("Secrets", function() { it("should encrypt with default key if keys is null", async function() { const key = new Uint8Array(16); for (let i = 0; i < 16; i++) key[i] = i; - const getKey = jest.fn(e => { + const getKey = jest.fn().mockImplementation(async e => { expect(Object.keys(e.keys)).toEqual([newKeyId]); return [newKeyId, key]; }); @@ -193,11 +198,12 @@ describe("Secrets", function() { content: contents, }), ]); + return {}; }; resetCrossSigningKeys(alice); const { keyId: newKeyId } = await alice.addSecretStorageKey( - SECRET_STORAGE_ALGORITHM_V1_AES, + SECRET_STORAGE_ALGORITHM_V1_AES, { pubkey: undefined, key: undefined }, ); // we don't await on this because it waits for the event to come down the sync // which won't happen in the test setup @@ -306,7 +312,7 @@ describe("Secrets", function() { it("bootstraps when no storage or cross-signing keys locally", async function() { const key = new Uint8Array(16); for (let i = 0; i < 16; i++) key[i] = i; - const getKey = jest.fn(e => { + const getKey = jest.fn().mockImplementation(async e => { return [Object.keys(e.keys)[0], key]; }); @@ -321,8 +327,8 @@ describe("Secrets", function() { }, }, ); - bob.uploadDeviceSigningKeys = async () => {}; - bob.uploadKeySignatures = async () => {}; + bob.uploadDeviceSigningKeys = async () => ({}); + bob.uploadKeySignatures = jest.fn().mockResolvedValue(undefined); bob.setAccountData = async function(eventType, contents, callback) { const event = new MatrixEvent({ type: eventType, @@ -332,10 +338,11 @@ describe("Secrets", function() { event, ]); this.emit("accountData", event); + return {}; }; await bob.bootstrapCrossSigning({ - authUploadDeviceSigningKeys: async func => await func({}), + authUploadDeviceSigningKeys: async func => { await func({}); }, }); await bob.bootstrapSecretStorage({ createSecretStorageKey, @@ -419,7 +426,7 @@ describe("Secrets", function() { }); it("adds passphrase checking if it's lacking", async function() { - let crossSigningKeys = { + let crossSigningKeys: Record = { master: XSK, user_signing: USK, self_signing: SSK, @@ -431,9 +438,9 @@ describe("Secrets", function() { { userId: "@alice:example.com", deviceId: "Osborne2" }, { cryptoCallbacks: { - getCrossSigningKey: t => crossSigningKeys[t], + getCrossSigningKey: async t => crossSigningKeys[t], saveCrossSigningKeys: k => crossSigningKeys = k, - getSecretStorageKey: ({ keys }, name) => { + getSecretStorageKey: async ({ keys }, name) => { for (const keyId of Object.keys(keys)) { if (secretStorageKeys[keyId]) { return [keyId, secretStorageKeys[keyId]]; @@ -489,6 +496,8 @@ describe("Secrets", function() { }), ]); alice.crypto.deviceList.storeCrossSigningForUser("@alice:example.com", { + firstUse: false, + crossSigningVerifiedBefore: false, keys: { master: { user_id: "@alice:example.com", @@ -529,14 +538,15 @@ describe("Secrets", function() { }); alice.store.storeAccountDataEvents([event]); this.emit("accountData", event); + return {}; }; - await alice.bootstrapSecretStorage(); + await alice.bootstrapSecretStorage({}); expect(alice.getAccountData("m.secret_storage.default_key").getContent()) .toEqual({ key: "key_id" }); const keyInfo = alice.getAccountData("m.secret_storage.key.key_id") - .getContent(); + .getContent() as ISecretStorageKeyInfo; expect(keyInfo.algorithm) .toEqual("m.secret_storage.v1.aes-hmac-sha2"); expect(keyInfo.passphrase).toEqual({ @@ -551,7 +561,7 @@ describe("Secrets", function() { alice.stopClient(); }); it("fixes backup keys in the wrong format", async function() { - let crossSigningKeys = { + let crossSigningKeys: Record = { master: XSK, user_signing: USK, self_signing: SSK, @@ -563,9 +573,9 @@ describe("Secrets", function() { { userId: "@alice:example.com", deviceId: "Osborne2" }, { cryptoCallbacks: { - getCrossSigningKey: t => crossSigningKeys[t], + getCrossSigningKey: async t => crossSigningKeys[t], saveCrossSigningKeys: k => crossSigningKeys = k, - getSecretStorageKey: ({ keys }, name) => { + getSecretStorageKey: async ({ keys }, name) => { for (const keyId of Object.keys(keys)) { if (secretStorageKeys[keyId]) { return [keyId, secretStorageKeys[keyId]]; @@ -630,6 +640,8 @@ describe("Secrets", function() { }), ]); alice.crypto.deviceList.storeCrossSigningForUser("@alice:example.com", { + firstUse: false, + crossSigningVerifiedBefore: false, keys: { master: { user_id: "@alice:example.com", @@ -670,9 +682,10 @@ describe("Secrets", function() { }); alice.store.storeAccountDataEvents([event]); this.emit("accountData", event); + return {}; }; - await alice.bootstrapSecretStorage(); + await alice.bootstrapSecretStorage({}); const backupKey = alice.getAccountData("m.megolm_backup.v1") .getContent();