1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-09 10:22:46 +03:00

Enable noImplicitAny (#2895)

* Stash noImplicitAny work

* Enable noImplicitAny

* Update olm

* Fun

* Fix msgid stuff

* Fix tests

* Attempt to fix Browserify
This commit is contained in:
Michael Telatynski
2022-12-06 18:21:44 +00:00
committed by GitHub
parent 6f81371e61
commit 8d018f9c2d
83 changed files with 1615 additions and 1428 deletions

View File

@@ -232,7 +232,7 @@ describe.each([
return store;
}],
])("CrossSigning > createCryptoStoreCacheCallbacks [%s]", function(name, dbFactory) {
let store;
let store: IndexedDBCryptoStore;
beforeAll(() => {
store = dbFactory();

View File

@@ -22,6 +22,7 @@ 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";
import { CryptoStore } from "../../../src/crypto/store/base";
const signedDeviceList: IDownloadKeyResult = {
"failures": {},
@@ -88,8 +89,8 @@ const signedDeviceList2: IDownloadKeyResult = {
};
describe('DeviceList', function() {
let downloadSpy;
let cryptoStore;
let downloadSpy: jest.Mock;
let cryptoStore: CryptoStore;
let deviceLists: DeviceList[] = [];
beforeEach(function() {
@@ -112,7 +113,7 @@ describe('DeviceList', function() {
deviceId: 'HGKAWHRVJQ',
} as unknown as MatrixClient;
const mockOlm = {
verifySignature: function(key, message, signature) {},
verifySignature: function(key: string, message: string, signature: string) {},
} as unknown as OlmDevice;
const dl = new DeviceList(baseApis, cryptoStore, mockOlm, keyDownloadChunkSize);
deviceLists.push(dl);

View File

@@ -17,6 +17,7 @@ limitations under the License.
import { mocked, MockedObject } from 'jest-mock';
import '../../../olm-loader';
import type { OutboundGroupSession } from "@matrix-org/olm";
import * as algorithms from "../../../../src/crypto/algorithms";
import { MemoryCryptoStore } from "../../../../src/crypto/store/memory-crypto-store";
import * as testUtils from "../../../test-utils/test-utils";
@@ -31,6 +32,7 @@ import { TypedEventEmitter } from '../../../../src/models/typed-event-emitter';
import { ClientEvent, MatrixClient, RoomMember } from '../../../../src';
import { DeviceInfo, IDevice } from '../../../../src/crypto/deviceinfo';
import { DeviceTrustLevel } from '../../../../src/crypto/CrossSigning';
import { MegolmEncryption as MegolmEncryptionClass } from "../../../../src/crypto/algorithms/megolm";
const MegolmDecryption = algorithms.DECRYPTION_CLASSES.get('m.megolm.v1.aes-sha2')!;
const MegolmEncryption = algorithms.ENCRYPTION_CLASSES.get('m.megolm.v1.aes-sha2')!;
@@ -87,7 +89,7 @@ describe("MegolmDecryption", function() {
});
describe('receives some keys:', function() {
let groupSession;
let groupSession: OutboundGroupSession;
beforeEach(async function() {
groupSession = new global.Olm.OutboundGroupSession();
groupSession.create();
@@ -298,10 +300,10 @@ describe("MegolmDecryption", function() {
describe("session reuse and key reshares", () => {
const rotationPeriodMs = 999 * 24 * 60 * 60 * 1000; // 999 days, so we don't have to deal with it
let megolmEncryption;
let aliceDeviceInfo;
let mockRoom;
let olmDevice;
let megolmEncryption: MegolmEncryptionClass;
let aliceDeviceInfo: DeviceInfo;
let mockRoom: Room;
let olmDevice: OlmDevice;
beforeEach(async () => {
// @ts-ignore assigning to readonly prop
@@ -342,7 +344,7 @@ describe("MegolmDecryption", function() {
'YWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWFhYWE',
),
getFingerprint: jest.fn().mockReturnValue(''),
};
} as unknown as DeviceInfo;
mockCrypto.downloadKeys.mockReturnValue(Promise.resolve({
'@alice:home.server': {
@@ -365,7 +367,7 @@ describe("MegolmDecryption", function() {
algorithm: 'm.megolm.v1.aes-sha2',
rotation_period_ms: rotationPeriodMs,
},
});
}) as MegolmEncryptionClass;
// Splice the real method onto the mock object as megolm uses this method
// on the crypto class in order to encrypt / start sessions
@@ -381,7 +383,7 @@ describe("MegolmDecryption", function() {
[{ userId: "@alice:home.server" }],
),
getBlacklistUnverifiedDevices: jest.fn().mockReturnValue(false),
};
} as unknown as Room;
});
it("should use larger otkTimeout when preparing to encrypt room", async () => {
@@ -397,11 +399,14 @@ describe("MegolmDecryption", function() {
});
it("should generate a new session if this one needs rotation", async () => {
// @ts-ignore - private method access
const session = await megolmEncryption.prepareNewSession(false);
session.creationTime -= rotationPeriodMs + 10000; // a smidge over the rotation time
// Inject expired session which needs rotation
// @ts-ignore - private field access
megolmEncryption.setupPromise = Promise.resolve(session);
// @ts-ignore - private method access
const prepareNewSessionSpy = jest.spyOn(megolmEncryption, "prepareNewSession");
await megolmEncryption.encryptMessage(mockRoom, "a.fake.type", {
body: "Some text",
@@ -446,8 +451,8 @@ describe("MegolmDecryption", function() {
});
mockBaseApis.sendToDevice.mockClear();
await megolmEncryption.reshareKeyWithDevice(
olmDevice.deviceCurve25519Key,
await megolmEncryption.reshareKeyWithDevice!(
olmDevice.deviceCurve25519Key!,
ct1.session_id,
'@alice:home.server',
aliceDeviceInfo,
@@ -466,8 +471,8 @@ describe("MegolmDecryption", function() {
);
mockBaseApis.queueToDevice.mockClear();
await megolmEncryption.reshareKeyWithDevice(
olmDevice.deviceCurve25519Key,
await megolmEncryption.reshareKeyWithDevice!(
olmDevice.deviceCurve25519Key!,
ct1.session_id,
'@alice:home.server',
aliceDeviceInfo,

View File

@@ -31,17 +31,21 @@ function makeOlmDevice() {
return olmDevice;
}
async function setupSession(initiator, opponent) {
async function setupSession(initiator: OlmDevice, opponent: OlmDevice) {
await opponent.generateOneTimeKeys(1);
const keys = await opponent.getOneTimeKeys();
const firstKey = Object.values(keys['curve25519'])[0];
const sid = await initiator.createOutboundSession(
opponent.deviceCurve25519Key, firstKey,
);
const sid = await initiator.createOutboundSession(opponent.deviceCurve25519Key!, firstKey);
return sid;
}
function alwaysSucceed<T>(promise: Promise<T>): Promise<T | void> {
// swallow any exception thrown by a promise, so that
// Promise.all doesn't abort
return promise.catch(() => {});
}
describe("OlmDevice", function() {
if (!global.Olm) {
logger.warn('Not running megolm unit tests: libolm not present');
@@ -159,11 +163,6 @@ describe("OlmDevice", function() {
}, "ABCDEFG"),
],
};
function alwaysSucceed(promise) {
// swallow any exception thrown by a promise, so that
// Promise.all doesn't abort
return promise.catch(() => {});
}
// start two tasks that try to ensure that there's an olm session
const promises = Promise.all([
@@ -235,12 +234,6 @@ describe("OlmDevice", function() {
],
};
function alwaysSucceed(promise) {
// swallow any exception thrown by a promise, so that
// Promise.all doesn't abort
return promise.catch(() => {});
}
const task1 = alwaysSucceed(olmlib.ensureOlmSessionsForDevices(
aliceOlmDevice, baseApis, devicesByUserAB,
));

View File

@@ -15,8 +15,6 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import { MockedObject } from "jest-mock";
import '../../olm-loader';
import { logger } from "../../../src/logger";
import * as olmlib from "../../../src/crypto/olmlib";
@@ -30,7 +28,10 @@ import { Crypto } from "../../../src/crypto";
import { resetCrossSigningKeys } from "./crypto-utils";
import { BackupManager } from "../../../src/crypto/backup";
import { StubStore } from "../../../src/store/stub";
import { MatrixScheduler } from '../../../src';
import { IndexedDBCryptoStore, MatrixScheduler } from '../../../src';
import { CryptoStore } from "../../../src/crypto/store/base";
import { MegolmDecryption as MegolmDecryptionClass } from "../../../src/crypto/algorithms/megolm";
import { IKeyBackupInfo } from "../../../src/crypto/keybackup";
const Olm = global.Olm;
@@ -102,29 +103,36 @@ const CURVE25519_BACKUP_INFO = {
},
};
const AES256_BACKUP_INFO = {
const AES256_BACKUP_INFO: IKeyBackupInfo = {
algorithm: "org.matrix.msc3270.v1.aes-hmac-sha2",
version: '1',
auth_data: {
// FIXME: add iv and mac
},
auth_data: {} as IKeyBackupInfo["auth_data"],
};
const keys = {};
const keys: Record<string, Uint8Array> = {};
function getCrossSigningKey(type) {
return keys[type];
function getCrossSigningKey(type: string) {
return Promise.resolve(keys[type]);
}
function saveCrossSigningKeys(k) {
function saveCrossSigningKeys(k: Record<string, Uint8Array>) {
Object.assign(keys, k);
}
function makeTestClient(cryptoStore) {
const scheduler = [
"getQueueForEvent", "queueEvent", "removeEventFromQueue",
function makeTestScheduler(): MatrixScheduler {
return ([
"getQueueForEvent",
"queueEvent",
"removeEventFromQueue",
"setProcessFunction",
].reduce((r, k) => {r[k] = jest.fn(); return r;}, {}) as MockedObject<MatrixScheduler>;
] as const).reduce((r, k) => {
r[k] = jest.fn();
return r;
}, {} as MatrixScheduler);
}
function makeTestClient(cryptoStore: CryptoStore) {
const scheduler = makeTestScheduler();
const store = new StubStore();
return new MatrixClient({
@@ -151,36 +159,33 @@ describe("MegolmBackup", function() {
return Olm.init();
});
let olmDevice;
let mockOlmLib;
let mockCrypto;
let cryptoStore;
let megolmDecryption;
let olmDevice: OlmDevice;
let mockOlmLib: typeof olmlib;
let mockCrypto: Crypto;
let cryptoStore: CryptoStore;
let megolmDecryption: MegolmDecryptionClass;
beforeEach(async function() {
mockCrypto = testUtils.mock(Crypto, 'Crypto');
// @ts-ignore making mock
mockCrypto.backupManager = testUtils.mock(BackupManager, "BackupManager");
mockCrypto.backupKey = new Olm.PkEncryption();
mockCrypto.backupKey.set_recipient_key(
"hSDwCYkwp1R0i33ctD73Wg2/Og0mOBr066SpjqqbTmo",
);
mockCrypto.backupInfo = CURVE25519_BACKUP_INFO;
mockCrypto.backupManager.backupInfo = CURVE25519_BACKUP_INFO;
cryptoStore = new MemoryCryptoStore();
olmDevice = new OlmDevice(cryptoStore);
// we stub out the olm encryption bits
mockOlmLib = {};
mockOlmLib = {} as unknown as typeof olmlib;
mockOlmLib.ensureOlmSessionsForDevices = jest.fn();
mockOlmLib.encryptMessageForDevice =
jest.fn().mockResolvedValue(undefined);
});
describe("backup", function() {
let mockBaseApis;
let mockBaseApis: MatrixClient;
beforeEach(function() {
mockBaseApis = {};
mockBaseApis = {} as unknown as MatrixClient;
megolmDecryption = new MegolmDecryption({
userId: '@user:id',
@@ -188,8 +193,9 @@ describe("MegolmBackup", function() {
olmDevice: olmDevice,
baseApis: mockBaseApis,
roomId: ROOM_ID,
});
}) as MegolmDecryptionClass;
// @ts-ignore private field access
megolmDecryption.olmlib = mockOlmLib;
// clobber the setTimeout function to run 100x faster.
@@ -239,6 +245,7 @@ describe("MegolmBackup", function() {
};
mockCrypto.cancelRoomKeyRequest = function() {};
// @ts-ignore readonly field write
mockCrypto.backupManager = {
backupGroupSession: jest.fn(),
};
@@ -264,21 +271,22 @@ describe("MegolmBackup", function() {
olmDevice: olmDevice,
baseApis: client,
roomId: ROOM_ID,
});
}) as MegolmDecryptionClass;
// @ts-ignore private field access
megolmDecryption.olmlib = mockOlmLib;
return client.initCrypto()
.then(() => {
return cryptoStore.doTxn(
"readwrite",
[cryptoStore.STORE_SESSION],
[IndexedDBCryptoStore.STORE_SESSIONS],
(txn) => {
cryptoStore.addEndToEndInboundGroupSession(
"F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI",
groupSession.session_id(),
{
forwardingCurve25519KeyChain: undefined,
forwardingCurve25519KeyChain: undefined!,
keysClaimed: {
ed25519: "SENDER_ED25519",
},
@@ -298,25 +306,25 @@ describe("MegolmBackup", function() {
});
let numCalls = 0;
return new Promise<void>((resolve, reject) => {
client.http.authedRequest = function<T>(
client.http.authedRequest = function(
method, path, queryParams, data, opts,
): Promise<T> {
): any {
++numCalls;
expect(numCalls).toBeLessThanOrEqual(1);
if (numCalls >= 2) {
// exit out of retry loop if there's something wrong
reject(new Error("authedRequest called too many timmes"));
return Promise.resolve({} as T);
return Promise.resolve({});
}
expect(method).toBe("PUT");
expect(path).toBe("/room_keys/keys");
expect(queryParams.version).toBe('1');
expect(queryParams?.version).toBe('1');
expect((data as Record<string, any>).rooms[ROOM_ID].sessions).toBeDefined();
expect((data as Record<string, any>).rooms[ROOM_ID].sessions).toHaveProperty(
groupSession.session_id(),
);
resolve();
return Promise.resolve({} as T);
return Promise.resolve({});
};
client.crypto!.backupManager.backupGroupSession(
"F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI",
@@ -343,8 +351,9 @@ describe("MegolmBackup", function() {
olmDevice: olmDevice,
baseApis: client,
roomId: ROOM_ID,
});
}) as MegolmDecryptionClass;
// @ts-ignore private field access
megolmDecryption.olmlib = mockOlmLib;
return client.initCrypto()
@@ -354,13 +363,13 @@ describe("MegolmBackup", function() {
.then(() => {
return cryptoStore.doTxn(
"readwrite",
[cryptoStore.STORE_SESSION],
[IndexedDBCryptoStore.STORE_SESSIONS],
(txn) => {
cryptoStore.addEndToEndInboundGroupSession(
"F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI",
groupSession.session_id(),
{
forwardingCurve25519KeyChain: undefined,
forwardingCurve25519KeyChain: undefined!,
keysClaimed: {
ed25519: "SENDER_ED25519",
},
@@ -381,25 +390,25 @@ describe("MegolmBackup", function() {
});
let numCalls = 0;
return new Promise<void>((resolve, reject) => {
client.http.authedRequest = function<T>(
client.http.authedRequest = function(
method, path, queryParams, data, opts,
): Promise<T> {
): any {
++numCalls;
expect(numCalls).toBeLessThanOrEqual(1);
if (numCalls >= 2) {
// exit out of retry loop if there's something wrong
reject(new Error("authedRequest called too many timmes"));
return Promise.resolve({} as T);
return Promise.resolve({});
}
expect(method).toBe("PUT");
expect(path).toBe("/room_keys/keys");
expect(queryParams.version).toBe('1');
expect(queryParams?.version).toBe('1');
expect((data as Record<string, any>).rooms[ROOM_ID].sessions).toBeDefined();
expect((data as Record<string, any>).rooms[ROOM_ID].sessions).toHaveProperty(
groupSession.session_id(),
);
resolve();
return Promise.resolve({} as T);
return Promise.resolve({});
};
client.crypto!.backupManager.backupGroupSession(
"F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI",
@@ -426,8 +435,9 @@ describe("MegolmBackup", function() {
olmDevice: olmDevice,
baseApis: client,
roomId: ROOM_ID,
});
}) as MegolmDecryptionClass;
// @ts-ignore private field access
megolmDecryption.olmlib = mockOlmLib;
await client.initCrypto();
@@ -437,10 +447,10 @@ describe("MegolmBackup", function() {
let numCalls = 0;
await Promise.all([
new Promise<void>((resolve, reject) => {
let backupInfo;
let backupInfo: Record<string, any> | BodyInit | undefined;
client.http.authedRequest = function(
method, path, queryParams, data, opts,
) {
): any {
++numCalls;
expect(numCalls).toBeLessThanOrEqual(2);
if (numCalls === 1) {
@@ -486,10 +496,7 @@ describe("MegolmBackup", function() {
const ibGroupSession = new Olm.InboundGroupSession();
ibGroupSession.create(groupSession.session_key());
const scheduler = [
"getQueueForEvent", "queueEvent", "removeEventFromQueue",
"setProcessFunction",
].reduce((r, k) => {r[k] = jest.fn(); return r;}, {}) as MockedObject<MatrixScheduler>;
const scheduler = makeTestScheduler();
const store = new StubStore();
const client = new MatrixClient({
baseUrl: "https://my.home.server",
@@ -509,20 +516,21 @@ describe("MegolmBackup", function() {
olmDevice: olmDevice,
baseApis: client,
roomId: ROOM_ID,
});
}) as MegolmDecryptionClass;
// @ts-ignore private field access
megolmDecryption.olmlib = mockOlmLib;
await client.initCrypto();
await cryptoStore.doTxn(
"readwrite",
[cryptoStore.STORE_SESSION],
[IndexedDBCryptoStore.STORE_SESSIONS],
(txn) => {
cryptoStore.addEndToEndInboundGroupSession(
"F0Q2NmyJNgUVj9DGsb4ZQt3aVxhVcUQhg7+gvW0oyKI",
groupSession.session_id(),
{
forwardingCurve25519KeyChain: undefined,
forwardingCurve25519KeyChain: undefined!,
keysClaimed: {
ed25519: "SENDER_ED25519",
},
@@ -542,26 +550,26 @@ describe("MegolmBackup", function() {
let numCalls = 0;
await new Promise<void>((resolve, reject) => {
client.http.authedRequest = function<T>(
client.http.authedRequest = function(
method, path, queryParams, data, opts,
): Promise<T> {
): any {
++numCalls;
expect(numCalls).toBeLessThanOrEqual(2);
if (numCalls >= 3) {
// exit out of retry loop if there's something wrong
reject(new Error("authedRequest called too many timmes"));
return Promise.resolve({} as T);
return Promise.resolve({});
}
expect(method).toBe("PUT");
expect(path).toBe("/room_keys/keys");
expect(queryParams.version).toBe('1');
expect(queryParams?.version).toBe('1');
expect((data as Record<string, any>).rooms[ROOM_ID].sessions).toBeDefined();
expect((data as Record<string, any>).rooms[ROOM_ID].sessions).toHaveProperty(
groupSession.session_id(),
);
if (numCalls > 1) {
resolve();
return Promise.resolve({} as T);
return Promise.resolve({});
} else {
return Promise.reject(
new Error("this is an expected failure"),
@@ -579,7 +587,7 @@ describe("MegolmBackup", function() {
});
describe("restore", function() {
let client;
let client: MatrixClient;
beforeEach(function() {
client = makeTestClient(cryptoStore);
@@ -590,8 +598,9 @@ describe("MegolmBackup", function() {
olmDevice: olmDevice,
baseApis: client,
roomId: ROOM_ID,
});
}) as MegolmDecryptionClass;
// @ts-ignore private field access
megolmDecryption.olmlib = mockOlmLib;
return client.initCrypto();
@@ -603,7 +612,7 @@ describe("MegolmBackup", function() {
it('can restore from backup (Curve25519 version)', function() {
client.http.authedRequest = function() {
return Promise.resolve(CURVE25519_KEY_BACKUP_DATA);
return Promise.resolve<any>(CURVE25519_KEY_BACKUP_DATA);
};
return client.restoreKeyBackupWithRecoveryKey(
"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d",
@@ -620,7 +629,7 @@ describe("MegolmBackup", function() {
it('can restore from backup (AES-256 version)', function() {
client.http.authedRequest = function() {
return Promise.resolve(AES256_KEY_BACKUP_DATA);
return Promise.resolve<any>(AES256_KEY_BACKUP_DATA);
};
return client.restoreKeyBackupWithRecoveryKey(
"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d",
@@ -637,7 +646,7 @@ describe("MegolmBackup", function() {
it('can restore backup by room (Curve25519 version)', function() {
client.http.authedRequest = function() {
return Promise.resolve({
return Promise.resolve<any>({
rooms: {
[ROOM_ID]: {
sessions: {
@@ -649,7 +658,7 @@ describe("MegolmBackup", function() {
};
return client.restoreKeyBackupWithRecoveryKey(
"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d",
null, null, CURVE25519_BACKUP_INFO,
null!, null!, CURVE25519_BACKUP_INFO,
).then(() => {
return megolmDecryption.decryptEvent(ENCRYPTED_EVENT);
}).then((res) => {
@@ -659,18 +668,18 @@ describe("MegolmBackup", function() {
it('has working cache functions', async function() {
const key = Uint8Array.from([1, 2, 3, 4, 5, 6, 7, 8]);
await client.crypto.storeSessionBackupPrivateKey(key);
const result = await client.crypto.getSessionBackupPrivateKey();
expect(new Uint8Array(result)).toEqual(key);
await client.crypto!.storeSessionBackupPrivateKey(key);
const result = await client.crypto!.getSessionBackupPrivateKey();
expect(new Uint8Array(result!)).toEqual(key);
});
it('caches session backup keys as it encounters them', async function() {
const cachedNull = await client.crypto.getSessionBackupPrivateKey();
const cachedNull = await client.crypto!.getSessionBackupPrivateKey();
expect(cachedNull).toBeNull();
client.http.authedRequest = function() {
return Promise.resolve(CURVE25519_KEY_BACKUP_DATA);
return Promise.resolve<any>(CURVE25519_KEY_BACKUP_DATA);
};
await new Promise((resolve) => {
await new Promise<void>((resolve) => {
client.restoreKeyBackupWithRecoveryKey(
"EsTc LW2K PGiF wKEA 3As5 g5c4 BXwk qeeJ ZJV8 Q9fu gUMN UE4d",
ROOM_ID,
@@ -679,7 +688,7 @@ describe("MegolmBackup", function() {
{ cacheCompleteCallback: resolve },
);
});
const cachedKey = await client.crypto.getSessionBackupPrivateKey();
const cachedKey = await client.crypto!.getSessionBackupPrivateKey();
expect(cachedKey).not.toBeNull();
});
@@ -688,7 +697,7 @@ describe("MegolmBackup", function() {
algorithm: "this.algorithm.does.not.exist",
});
client.http.authedRequest = function() {
return Promise.resolve(CURVE25519_KEY_BACKUP_DATA);
return Promise.resolve<any>(CURVE25519_KEY_BACKUP_DATA);
};
await expect(client.restoreKeyBackupWithRecoveryKey(
@@ -702,10 +711,7 @@ describe("MegolmBackup", function() {
describe("flagAllGroupSessionsForBackup", () => {
it("should return number of sesions needing backup", async () => {
const scheduler = [
"getQueueForEvent", "queueEvent", "removeEventFromQueue",
"setProcessFunction",
].reduce((r, k) => {r[k] = jest.fn(); return r;}, {}) as MockedObject<MatrixScheduler>;
const scheduler = makeTestScheduler();
const store = new StubStore();
const client = new MatrixClient({
baseUrl: "https://my.home.server",

View File

@@ -18,23 +18,24 @@ limitations under the License.
import '../../olm-loader';
import anotherjson from 'another-json';
import { PkSigning } from '@matrix-org/olm';
import HttpBackend from "matrix-mock-request";
import * as olmlib from "../../../src/crypto/olmlib";
import { MatrixError } from '../../../src/http-api';
import { logger } from '../../../src/logger';
import { ICrossSigningKey, ICreateClientOpts, ISignedKey } from '../../../src/client';
import { CryptoEvent } from '../../../src/crypto';
import { CryptoEvent, IBootstrapCrossSigningOpts } from '../../../src/crypto';
import { IDevice } from '../../../src/crypto/deviceinfo';
import { TestClient } from '../../TestClient';
import { resetCrossSigningKeys } from "./crypto-utils";
const PUSH_RULES_RESPONSE = {
const PUSH_RULES_RESPONSE: Response = {
method: "GET",
path: "/pushrules/",
data: {},
};
const filterResponse = function(userId) {
const filterResponse = function(userId: string): Response {
const filterPath = "/user/" + encodeURIComponent(userId) + "/filter";
return {
method: "POST",
@@ -43,7 +44,13 @@ const filterResponse = function(userId) {
};
};
function setHttpResponses(httpBackend, responses) {
interface Response {
method: 'GET' | 'PUT' | 'POST' | 'DELETE';
path: string;
data: object;
}
function setHttpResponses(httpBackend: HttpBackend, responses: Response[]) {
responses.forEach(response => {
httpBackend
.when(response.method, response.path)
@@ -54,13 +61,13 @@ function setHttpResponses(httpBackend, responses) {
async function makeTestClient(
userInfo: { userId: string, deviceId: string},
options: Partial<ICreateClientOpts> = {},
keys = {},
keys: Record<string, Uint8Array> = {},
) {
function getCrossSigningKey(type) {
return keys[type];
function getCrossSigningKey(type: string) {
return keys[type] ?? null;
}
function saveCrossSigningKeys(k) {
function saveCrossSigningKeys(k: Record<string, Uint8Array>) {
Object.assign(keys, k);
}
@@ -142,7 +149,9 @@ describe("Cross Signing", function() {
alice.uploadKeySignatures = async () => ({ failures: {} });
alice.setAccountData = async () => ({});
alice.getAccountDataFromServer = async <T extends {[k: string]: any}>(): Promise<T | null> => ({} as T);
const authUploadDeviceSigningKeys = async func => await func({});
const authUploadDeviceSigningKeys: IBootstrapCrossSigningOpts["authUploadDeviceSigningKeys"] = async func => {
await func({});
};
// Try bootstrap, expecting `authUploadDeviceSigningKeys` to pass
// through failure, stopping before actually applying changes.
@@ -275,7 +284,7 @@ describe("Cross Signing", function() {
);
// feed sync result that includes master key, ssk, device key
const responses = [
const responses: Response[] = [
PUSH_RULES_RESPONSE,
{
method: "POST",
@@ -464,7 +473,7 @@ describe("Cross Signing", function() {
});
it.skip("should trust signatures received from other devices", async function() {
const aliceKeys: Record<string, PkSigning> = {};
const aliceKeys: Record<string, Uint8Array> = {};
const { client: alice, httpBackend } = await makeTestClient(
{ userId: "@alice:example.com", deviceId: "Osborne2" },
undefined,
@@ -494,8 +503,7 @@ describe("Cross Signing", function() {
});
// @ts-ignore private property
const deviceInfo = alice.crypto!.deviceList.devices["@alice:example.com"]
.Osborne2;
const deviceInfo = alice.crypto!.deviceList.devices["@alice:example.com"].Osborne2;
const aliceDevice = {
user_id: "@alice:example.com",
device_id: "Osborne2",
@@ -549,7 +557,7 @@ describe("Cross Signing", function() {
// - ssk
// - master key signed by her usk (pretend that it was signed by another
// of Alice's devices)
const responses = [
const responses: Response[] = [
PUSH_RULES_RESPONSE,
{
method: "POST",
@@ -853,7 +861,7 @@ describe("Cross Signing", function() {
});
it("should offer to upgrade device verifications to cross-signing", async function() {
let upgradeResolveFunc;
let upgradeResolveFunc: Function;
const { client: alice } = await makeTestClient(
{ userId: "@alice:example.com", deviceId: "Osborne2" },

View File

@@ -1,14 +1,16 @@
import { IRecoveryKey } from '../../../src/crypto/api';
import { CrossSigningLevel } from '../../../src/crypto/CrossSigning';
import { IndexedDBCryptoStore } from '../../../src/crypto/store/indexeddb-crypto-store';
import { MatrixClient } from "../../../src";
import { CryptoEvent } from "../../../src/crypto";
// needs to be phased out and replaced with bootstrapSecretStorage,
// but that is doing too much extra stuff for it to be an easy transition.
export async function resetCrossSigningKeys(
client,
client: MatrixClient,
{ level }: { level?: CrossSigningLevel} = {},
): Promise<void> {
const crypto = client.crypto;
const crypto = client.crypto!;
const oldKeys = Object.assign({}, crypto.crossSigningInfo.keys);
try {
@@ -28,7 +30,8 @@ export async function resetCrossSigningKeys(
crypto.crossSigningInfo.keys = oldKeys;
throw e;
}
crypto.emit("crossSigning.keysChanged", {});
crypto.emit(CryptoEvent.KeysChanged, {});
// @ts-ignore
await crypto.afterCrossSigningLocalKeyChange();
}

View File

@@ -16,6 +16,7 @@ limitations under the License.
import '../../olm-loader';
import * as olmlib from "../../../src/crypto/olmlib";
import { IObject } from "../../../src/crypto/olmlib";
import { SECRET_STORAGE_ALGORITHM_V1_AES } from "../../../src/crypto/SecretStorage";
import { MatrixEvent } from "../../../src/models/event";
import { TestClient } from '../../TestClient';
@@ -23,9 +24,11 @@ import { makeTestClients } from './verification/util';
import { encryptAES } from "../../../src/crypto/aes";
import { createSecretStorageKey, resetCrossSigningKeys } from "./crypto-utils";
import { logger } from '../../../src/logger';
import { ClientEvent, ICreateClientOpts } from '../../../src/client';
import { ClientEvent, ICreateClientOpts, ICrossSigningKey, MatrixClient } from '../../../src/client';
import { ISecretStorageKeyInfo } from '../../../src/crypto/api';
import { DeviceInfo } from '../../../src/crypto/deviceinfo';
import { ISignatures } from "../../../src/@types/signed";
import { ICurve25519AuthData } from "../../../src/crypto/keybackup";
async function makeTestClient(userInfo: { userId: string, deviceId: string}, options: Partial<ICreateClientOpts> = {}) {
const client = (new TestClient(
@@ -48,9 +51,15 @@ async function makeTestClient(userInfo: { userId: string, deviceId: string}, opt
// Wrapper around pkSign to return a signed object. pkSign returns the
// signature, rather than the signed object.
function sign(obj, key, userId) {
function sign<T extends IObject | ICurve25519AuthData>(obj: T, key: Uint8Array, userId: string): T & {
signatures: ISignatures;
unsigned?: object;
} {
olmlib.pkSign(obj, key, userId, '');
return obj;
return obj as T & {
signatures: ISignatures;
unsigned?: object;
};
}
describe("Secrets", function() {
@@ -169,12 +178,12 @@ describe("Secrets", function() {
return [newKeyId, key];
});
let keys = {};
let keys: Record<string, Uint8Array> = {};
const alice = await makeTestClient(
{ userId: "@alice:example.com", deviceId: "Osborne2" },
{
cryptoCallbacks: {
getCrossSigningKey: t => keys[t],
getCrossSigningKey: t => Promise.resolve(keys[t]),
saveCrossSigningKeys: k => keys = k,
getSecretStorageKey: getKey,
},
@@ -227,7 +236,7 @@ describe("Secrets", function() {
cryptoCallbacks: {
onSecretRequested: (userId, deviceId, requestId, secretName, deviceTrust) => {
expect(secretName).toBe("foo");
return "bar";
return Promise.resolve("bar");
},
},
},
@@ -354,7 +363,7 @@ describe("Secrets", function() {
const storagePublicKey = decryption.generate_key();
const storagePrivateKey = decryption.get_private_key();
const bob = await makeTestClient(
const bob: MatrixClient = await makeTestClient(
{
userId: "@bob:example.com",
deviceId: "bob1",
@@ -364,15 +373,15 @@ describe("Secrets", function() {
getSecretStorageKey: async request => {
const defaultKeyId = await bob.getDefaultSecretStorageKeyId();
expect(Object.keys(request.keys)).toEqual([defaultKeyId]);
return [defaultKeyId, storagePrivateKey];
return [defaultKeyId!, storagePrivateKey];
},
},
},
);
bob.uploadDeviceSigningKeys = async () => {};
bob.uploadKeySignatures = async () => {};
bob.setAccountData = async function(eventType, contents, callback) {
bob.uploadDeviceSigningKeys = async () => ({});
bob.uploadKeySignatures = async () => ({ failures: {} });
bob.setAccountData = async function(eventType, contents) {
const event = new MatrixEvent({
type: eventType,
content: contents,
@@ -380,16 +389,19 @@ describe("Secrets", function() {
this.store.storeAccountDataEvents([
event,
]);
this.emit("accountData", event);
this.emit(ClientEvent.AccountData, event);
return {};
};
bob.crypto.backupManager.checkKeyBackup = async () => {};
bob.crypto!.backupManager.checkKeyBackup = async () => null;
const crossSigning = bob.crypto.crossSigningInfo;
const secretStorage = bob.crypto.secretStorage;
const crossSigning = bob.crypto!.crossSigningInfo;
const secretStorage = bob.crypto!.secretStorage;
// Set up cross-signing keys from scratch with specific storage key
await bob.bootstrapCrossSigning({
authUploadDeviceSigningKeys: async func => await func({}),
authUploadDeviceSigningKeys: async func => {
await func({});
},
});
await bob.bootstrapSecretStorage({
createSecretStorageKey: async () => ({
@@ -400,13 +412,15 @@ describe("Secrets", function() {
});
// Clear local cross-signing keys and read from secret storage
bob.crypto.deviceList.storeCrossSigningForUser(
bob.crypto!.deviceList.storeCrossSigningForUser(
"@bob:example.com",
crossSigning.toStorage(),
);
crossSigning.keys = {};
await bob.bootstrapCrossSigning({
authUploadDeviceSigningKeys: async func => await func({}),
authUploadDeviceSigningKeys: async func => {
await func({});
},
});
expect(crossSigning.getId()).toBeTruthy();
@@ -422,7 +436,7 @@ describe("Secrets", function() {
user_signing: USK,
self_signing: SSK,
};
const secretStorageKeys = {
const secretStorageKeys: Record<string, Uint8Array> = {
key_id: SSSSKey,
};
const alice = await makeTestClient(
@@ -498,14 +512,14 @@ describe("Secrets", function() {
[`ed25519:${XSPubKey}`]: XSPubKey,
},
},
self_signing: sign({
self_signing: sign<ICrossSigningKey>({
user_id: "@alice:example.com",
usage: ["self_signing"],
keys: {
[`ed25519:${SSPubKey}`]: SSPubKey,
},
}, XSK, "@alice:example.com"),
user_signing: sign({
user_signing: sign<ICrossSigningKey>({
user_id: "@alice:example.com",
usage: ["user_signing"],
keys: {
@@ -557,7 +571,7 @@ describe("Secrets", function() {
user_signing: USK,
self_signing: SSK,
};
const secretStorageKeys = {
const secretStorageKeys: Record<string, Uint8Array> = {
key_id: SSSSKey,
};
const alice = await makeTestClient(
@@ -642,14 +656,14 @@ describe("Secrets", function() {
[`ed25519:${XSPubKey}`]: XSPubKey,
},
},
self_signing: sign({
self_signing: sign<ICrossSigningKey>({
user_id: "@alice:example.com",
usage: ["self_signing"],
keys: {
[`ed25519:${SSPubKey}`]: SSPubKey,
},
}, XSK, "@alice:example.com"),
user_signing: sign({
user_signing: sign<ICrossSigningKey>({
user_id: "@alice:example.com",
usage: ["user_signing"],
keys: {

View File

@@ -18,12 +18,12 @@ import "../../../olm-loader";
import { makeTestClients } from './util';
import { MatrixEvent } from "../../../../src/models/event";
import { ISasEvent, SAS, SasEvent } from "../../../../src/crypto/verification/SAS";
import { DeviceInfo } from "../../../../src/crypto/deviceinfo";
import { DeviceInfo, IDevice } from "../../../../src/crypto/deviceinfo";
import { CryptoEvent, verificationMethods } from "../../../../src/crypto";
import * as olmlib from "../../../../src/crypto/olmlib";
import { logger } from "../../../../src/logger";
import { resetCrossSigningKeys } from "../crypto-utils";
import { VerificationBase as Verification, VerificationBase } from "../../../../src/crypto/verification/Base";
import { VerificationBase } from "../../../../src/crypto/verification/Base";
import { IVerificationChannel } from "../../../../src/crypto/verification/request/Channel";
import { MatrixClient } from "../../../../src";
import { VerificationRequest } from "../../../../src/crypto/verification/request/VerificationRequest";
@@ -31,8 +31,8 @@ import { TestClient } from "../../../TestClient";
const Olm = global.Olm;
let ALICE_DEVICES;
let BOB_DEVICES;
let ALICE_DEVICES: Record<string, IDevice>;
let BOB_DEVICES: Record<string, IDevice>;
describe("SAS verification", function() {
if (!global.Olm) {
@@ -75,7 +75,7 @@ describe("SAS verification", function() {
let bob: TestClient;
let aliceSasEvent: ISasEvent | null;
let bobSasEvent: ISasEvent | null;
let aliceVerifier: Verification<any, any>;
let aliceVerifier: SAS;
let bobPromise: Promise<VerificationBase<any, any>>;
let clearTestClientTimeouts: () => void;
@@ -95,25 +95,25 @@ describe("SAS verification", function() {
ALICE_DEVICES = {
Osborne2: {
user_id: "@alice:example.com",
device_id: "Osborne2",
algorithms: [olmlib.OLM_ALGORITHM, olmlib.MEGOLM_ALGORITHM],
keys: {
"ed25519:Osborne2": aliceDevice.deviceEd25519Key,
"curve25519:Osborne2": aliceDevice.deviceCurve25519Key,
"ed25519:Osborne2": aliceDevice.deviceEd25519Key!,
"curve25519:Osborne2": aliceDevice.deviceCurve25519Key!,
},
verified: DeviceInfo.DeviceVerification.UNVERIFIED,
known: false,
},
};
BOB_DEVICES = {
Dynabook: {
user_id: "@bob:example.com",
device_id: "Dynabook",
algorithms: [olmlib.OLM_ALGORITHM, olmlib.MEGOLM_ALGORITHM],
keys: {
"ed25519:Dynabook": bobDevice.deviceEd25519Key,
"curve25519:Dynabook": bobDevice.deviceCurve25519Key,
"ed25519:Dynabook": bobDevice.deviceEd25519Key!,
"curve25519:Dynabook": bobDevice.deviceCurve25519Key!,
},
verified: DeviceInfo.DeviceVerification.UNVERIFIED,
known: false,
},
};
@@ -136,7 +136,7 @@ describe("SAS verification", function() {
bobPromise = new Promise<VerificationBase<any, any>>((resolve, reject) => {
bob.client.on(CryptoEvent.VerificationRequest, request => {
request.verifier!.on("show_sas", (e) => {
(<SAS>request.verifier!).on(SasEvent.ShowSas, (e) => {
if (!e.sas.emoji || !e.sas.decimal) {
e.cancel();
} else if (!aliceSasEvent) {
@@ -158,7 +158,7 @@ describe("SAS verification", function() {
aliceVerifier = alice.client.beginKeyVerification(
verificationMethods.SAS, bob.client.getUserId()!, bob.deviceId!,
);
) as SAS;
aliceVerifier.on(SasEvent.ShowSas, (e) => {
if (!e.sas.emoji || !e.sas.decimal) {
e.cancel();
@@ -413,7 +413,7 @@ describe("SAS verification", function() {
const bobPromise = new Promise<VerificationBase<any, any>>((resolve, reject) => {
bob.client.on(CryptoEvent.VerificationRequest, request => {
request.verifier!.on("show_sas", (e) => {
(<SAS>request.verifier!).on(SasEvent.ShowSas, (e) => {
e.mismatch();
});
resolve(request.verifier!);
@@ -443,13 +443,13 @@ describe("SAS verification", function() {
});
describe("verification in DM", function() {
let alice;
let bob;
let aliceSasEvent;
let bobSasEvent;
let aliceVerifier;
let bobPromise;
let clearTestClientTimeouts;
let alice: TestClient;
let bob: TestClient;
let aliceSasEvent: ISasEvent | null;
let bobSasEvent: ISasEvent | null;
let aliceVerifier: SAS;
let bobPromise: Promise<void>;
let clearTestClientTimeouts: Function;
beforeEach(async function() {
[[alice, bob], clearTestClientTimeouts] = await makeTestClients(
@@ -477,7 +477,7 @@ describe("SAS verification", function() {
);
};
alice.client.downloadKeys = () => {
return Promise.resolve();
return Promise.resolve({});
};
bob.client.crypto!.setDeviceVerification = jest.fn();
@@ -495,16 +495,16 @@ describe("SAS verification", function() {
return "bob+base64+ed25519+key";
};
bob.client.downloadKeys = () => {
return Promise.resolve();
return Promise.resolve({});
};
aliceSasEvent = null;
bobSasEvent = null;
bobPromise = new Promise<void>((resolve, reject) => {
bob.client.on("crypto.verification.request", async (request) => {
const verifier = request.beginKeyVerification(SAS.NAME);
verifier.on("show_sas", (e) => {
bob.client.on(CryptoEvent.VerificationRequest, async (request) => {
const verifier = request.beginKeyVerification(SAS.NAME) as SAS;
verifier.on(SasEvent.ShowSas, (e) => {
if (!e.sas.emoji || !e.sas.decimal) {
e.cancel();
} else if (!aliceSasEvent) {
@@ -525,12 +525,10 @@ describe("SAS verification", function() {
});
});
const aliceRequest = await alice.client.requestVerificationDM(
bob.client.getUserId(), "!room_id",
);
const aliceRequest = await alice.client.requestVerificationDM(bob.client.getUserId()!, "!room_id");
await aliceRequest.waitFor(r => r.started);
aliceVerifier = aliceRequest.verifier;
aliceVerifier.on("show_sas", (e) => {
aliceVerifier = aliceRequest.verifier! as SAS;
aliceVerifier.on(SasEvent.ShowSas, (e) => {
if (!e.sas.emoji || !e.sas.decimal) {
e.cancel();
} else if (!bobSasEvent) {

View File

@@ -125,7 +125,7 @@ describe("self-verifications", () => {
expect(restoreKeyBackupWithCache).toHaveBeenCalled();
expect(result).toBeInstanceOf(Array);
expect(result[0][0]).toBe(testKeyPub);
expect(result[1][0]).toBe(testKeyPub);
expect(result![0][0]).toBe(testKeyPub);
expect(result![1][0]).toBe(testKeyPub);
});
});

View File

@@ -16,13 +16,21 @@ limitations under the License.
*/
import { TestClient } from '../../../TestClient';
import { MatrixEvent } from "../../../../src/models/event";
import { IContent, MatrixEvent } from "../../../../src/models/event";
import { IRoomTimelineData } from "../../../../src/models/event-timeline-set";
import { Room, RoomEvent } from "../../../../src/models/room";
import { logger } from '../../../../src/logger';
import { MatrixClient, ClientEvent } from '../../../../src/client';
import { MatrixClient, ClientEvent, ICreateClientOpts } from '../../../../src/client';
export async function makeTestClients(userInfos, options): Promise<[TestClient[], () => void]> {
interface UserInfo {
userId: string;
deviceId: string;
}
export async function makeTestClients(
userInfos: UserInfo[],
options: Partial<ICreateClientOpts>,
): Promise<[TestClient[], () => void]> {
const clients: TestClient[] = [];
const timeouts: ReturnType<typeof setTimeout>[] = [];
const clientMap: Record<string, Record<string, MatrixClient>> = {};
@@ -51,7 +59,7 @@ export async function makeTestClients(userInfos, options): Promise<[TestClient[]
}
return {};
};
const makeSendEvent = (matrixClient: MatrixClient) => (room, type, content) => {
const makeSendEvent = (matrixClient: MatrixClient) => (room: string, type: string, content: IContent) => {
// make up a unique ID as the event ID
const eventId = "$" + matrixClient.makeTxnId();
const rawEvent = {
@@ -88,11 +96,12 @@ export async function makeTestClients(userInfos, options): Promise<[TestClient[]
};
for (const userInfo of userInfos) {
let keys = {};
let keys: Record<string, Uint8Array> = {};
if (!options) options = {};
if (!options.cryptoCallbacks) options.cryptoCallbacks = {};
if (!options.cryptoCallbacks.saveCrossSigningKeys) {
options.cryptoCallbacks.saveCrossSigningKeys = k => { keys = k; };
// @ts-ignore tsc getting confused by overloads
options.cryptoCallbacks.getCrossSigningKey = typ => keys[typ];
}
const testClient = new TestClient(
@@ -104,6 +113,7 @@ export async function makeTestClients(userInfos, options): Promise<[TestClient[]
}
clientMap[userInfo.userId][userInfo.deviceId] = testClient.client;
testClient.client.sendToDevice = makeSendToDevice(testClient.client);
// @ts-ignore tsc getting confused by overloads
testClient.client.sendEvent = makeSendEvent(testClient.client);
clients.push(testClient);
}

View File

@@ -18,7 +18,7 @@ import { VerificationRequest, READY_TYPE, START_TYPE, DONE_TYPE } from
import { InRoomChannel } from "../../../../src/crypto/verification/request/InRoomChannel";
import { ToDeviceChannel } from
"../../../../src/crypto/verification/request/ToDeviceChannel";
import { MatrixEvent } from "../../../../src/models/event";
import { IContent, MatrixEvent } from "../../../../src/models/event";
import { MatrixClient } from "../../../../src/client";
import { IVerificationChannel } from "../../../../src/crypto/verification/request/Channel";
import { VerificationBase } from "../../../../src/crypto/verification/Base";
@@ -30,12 +30,12 @@ type MockClient = MatrixClient & {
function makeMockClient(userId: string, deviceId: string): MockClient {
let counter = 1;
let events: MatrixEvent[] = [];
const deviceEvents = {};
const deviceEvents: Record<string, Record<string, MatrixEvent[]>> = {};
return {
getUserId() { return userId; },
getDeviceId() { return deviceId; },
sendEvent(roomId, type, content) {
sendEvent(roomId: string, type: string, content: IContent) {
counter = counter + 1;
const eventId = `$${userId}-${deviceId}-${counter}`;
events.push(new MatrixEvent({
@@ -49,7 +49,7 @@ function makeMockClient(userId: string, deviceId: string): MockClient {
return Promise.resolve({ event_id: eventId });
},
sendToDevice(type, msgMap) {
sendToDevice(type: string, msgMap: Record<string, Record<string, IContent>>) {
for (const userId of Object.keys(msgMap)) {
const deviceMap = msgMap[userId];
for (const deviceId of Object.keys(deviceMap)) {
@@ -111,7 +111,7 @@ class MockVerifier extends VerificationBase<'', any> {
}
}
async handleEvent(event) {
async handleEvent(event: MatrixEvent) {
if (event.getType() === DONE_TYPE && !this._startEvent) {
await this._channel.send(DONE_TYPE, {});
}
@@ -122,7 +122,7 @@ class MockVerifier extends VerificationBase<'', any> {
}
}
function makeRemoteEcho(event) {
function makeRemoteEcho(event: MatrixEvent) {
return new MatrixEvent(Object.assign({}, event.event, {
unsigned: {
transaction_id: "abc",