diff --git a/spec/unit/models/MSC3089Branch.spec.ts b/spec/unit/models/MSC3089Branch.spec.ts index d782a4276..1daf1c07f 100644 --- a/spec/unit/models/MSC3089Branch.spec.ts +++ b/spec/unit/models/MSC3089Branch.spec.ts @@ -312,7 +312,7 @@ describe("MSC3089Branch", () => { } as MatrixEvent); const events = [await branch.getFileEvent(), await branch2.getFileEvent(), { - replacingEventId: () => null, + replacingEventId: (): string => null, getId: () => "$unknown", }]; staticRoom.getLiveTimeline = () => ({ getEvents: () => events }) as EventTimeline; diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 0a857b4e8..c7e65ff77 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -62,12 +62,6 @@ declare global { }; } - interface HTMLAudioElement { - // sinkId & setSinkId are experimental and typescript doesn't know about them - sinkId: string; - setSinkId(outputId: string); - } - interface DummyInterfaceWeShouldntBeUsingThis {} interface Navigator { diff --git a/src/client.ts b/src/client.ts index f9a12f332..1171ddbee 100644 --- a/src/client.ts +++ b/src/client.ts @@ -621,7 +621,7 @@ export interface IMyDevice { last_seen_ts?: number; } -interface IDownloadKeyResult { +export interface IDownloadKeyResult { failures: { [serverName: string]: object }; device_keys: { [userId: string]: { @@ -632,13 +632,42 @@ interface IDownloadKeyResult { }; }; }; + // the following three fields were added in 1.1 + master_keys?: { + [userId: string]: { + keys: { [keyId: string]: string }; + usage: string[]; + user_id: string; + }; + }; + self_signing_keys?: { + [userId: string]: { + keys: { [keyId: string]: string }; + signatures: ISignatures; + usage: string[]; + user_id: string; + }; + }; + user_signing_keys?: { + [userId: string]: { + keys: { [keyId: string]: string }; + signatures: ISignatures; + usage: string[]; + user_id: string; + }; + }; } -interface IClaimOTKsResult { +export interface IClaimOTKsResult { failures: { [serverName: string]: object }; one_time_keys: { [userId: string]: { - [deviceId: string]: string; + [deviceId: string]: { + [keyId: string]: { + key: string; + signatures: ISignatures; + }; + }; }; }; } @@ -1421,14 +1450,12 @@ export class MatrixClient extends EventEmitter { } } - // We swallow errors because we need a default object anyhow return this.http.authedRequest( undefined, "GET", "/capabilities", - ).catch((e: Error) => { + ).catch((e: Error): void => { + // We swallow errors because we need a default object anyhow logger.error(e); - return null; // otherwise consume the error - }).then((r) => { - if (!r) r = {}; + }).then((r: { capabilities?: ICapabilities } = {}) => { const capabilities: ICapabilities = r["capabilities"] || {}; // If the capabilities missed the cache, cache it for a shorter amount @@ -2836,7 +2863,7 @@ export class MatrixClient extends EventEmitter { } let totalKeyCount = 0; - let keys = []; + let keys: IMegolmSessionData[] = []; const path = this.makeKeyBackupPath(targetRoomId, targetSessionId, backupInfo.version); @@ -5692,18 +5719,14 @@ export class MatrixClient extends EventEmitter { searchResults.count = roomEvents.count; searchResults.next_batch = roomEvents.next_batch; - // combine the highlight list with our existing list; build an object - // to avoid O(N^2) fail - const highlights = {}; - roomEvents.highlights.forEach((hl) => { - highlights[hl] = 1; - }); + // combine the highlight list with our existing list; + const highlights = new Set(roomEvents.highlights); searchResults.highlights.forEach((hl) => { - highlights[hl] = 1; + highlights.add(hl); }); // turn it back into a list. - searchResults.highlights = Object.keys(highlights); + searchResults.highlights = Array.from(highlights); // append the new results to our existing results const resultsLength = roomEvents.results ? roomEvents.results.length : 0; @@ -5794,11 +5817,9 @@ export class MatrixClient extends EventEmitter { return this.http.authedRequest( undefined, "GET", path, undefined, undefined, - ).then((response) => { + ).then((response: IFilterDefinition) => { // persist the filter - const filter = Filter.fromJson( - userId, filterId, response, - ); + const filter = Filter.fromJson(userId, filterId, response); this.store.storeFilter(filter); return filter; }); @@ -6096,7 +6117,7 @@ export class MatrixClient extends EventEmitter { { prefix: '', }, - ).catch((e) => { + ).catch((e: Error) => { // Need to unset this if it fails, otherwise we'll never retry this.serverVersionsPromise = null; // but rethrow the exception to anything that was waiting @@ -6420,7 +6441,7 @@ export class MatrixClient extends EventEmitter { public isUsernameAvailable(username: string): Promise { return this.http.authedRequest( undefined, "GET", '/register/available', { username: username }, - ).then((response) => { + ).then((response: { available: boolean }) => { return response.available; }); } @@ -7741,11 +7762,11 @@ export class MatrixClient extends EventEmitter { * an error response ({@link module:http-api.MatrixError}). */ public claimOneTimeKeys( - devices: string[], + devices: [string, string][], keyAlgorithm = "signed_curve25519", timeout?: number, ): Promise { - const queries = {}; + const queries: Record> = {}; if (keyAlgorithm === undefined) { keyAlgorithm = "signed_curve25519"; diff --git a/src/crypto/CrossSigning.ts b/src/crypto/CrossSigning.ts index 8eda069a4..077d705b8 100644 --- a/src/crypto/CrossSigning.ts +++ b/src/crypto/CrossSigning.ts @@ -33,6 +33,7 @@ import { OlmDevice } from "./OlmDevice"; import { ICryptoCallbacks } from "../matrix"; import { ISignatures } from "../@types/signed"; import { CryptoStore } from "./store/base"; +import { ISecretStorageKeyInfo } from "./api"; const KEY_REQUEST_TIMEOUT_MS = 1000 * 60; @@ -175,7 +176,7 @@ export class CrossSigningInfo extends EventEmitter { // check what SSSS keys have encrypted the master key (if any) const stored = await secretStorage.isStored("m.cross_signing.master", false) || {}; // then check which of those SSSS keys have also encrypted the SSK and USK - function intersect(s) { + function intersect(s: Record) { for (const k of Object.keys(stored)) { if (!s[k]) { delete stored[k]; diff --git a/src/crypto/DeviceList.ts b/src/crypto/DeviceList.ts index c8a87cc56..6e951263c 100644 --- a/src/crypto/DeviceList.ts +++ b/src/crypto/DeviceList.ts @@ -28,7 +28,7 @@ import { CrossSigningInfo, ICrossSigningInfo } from './CrossSigning'; import * as olmlib from './olmlib'; import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store'; import { chunkPromises, defer, IDeferred, sleep } from '../utils'; -import { MatrixClient } from "../client"; +import { IDownloadKeyResult, MatrixClient } from "../client"; import { OlmDevice } from "./OlmDevice"; import { CryptoStore } from "./store/base"; @@ -756,17 +756,21 @@ class DeviceListUpdateSerialiser { opts.token = this.syncToken; } - const factories = []; + const factories: Array<() => Promise> = []; for (let i = 0; i < downloadUsers.length; i += this.deviceList.keyDownloadChunkSize) { const userSlice = downloadUsers.slice(i, i + this.deviceList.keyDownloadChunkSize); factories.push(() => this.baseApis.downloadKeysForUsers(userSlice, opts)); } - chunkPromises(factories, 3).then(async (responses: any[]) => { - const dk = Object.assign({}, ...(responses.map(res => res.device_keys || {}))); - const masterKeys = Object.assign({}, ...(responses.map(res => res.master_keys || {}))); - const ssks = Object.assign({}, ...(responses.map(res => res.self_signing_keys || {}))); - const usks = Object.assign({}, ...(responses.map(res => res.user_signing_keys || {}))); + chunkPromises(factories, 3).then(async (responses: IDownloadKeyResult[]) => { + const dk: IDownloadKeyResult["device_keys"] + = Object.assign({}, ...(responses.map(res => res.device_keys || {}))); + const masterKeys: IDownloadKeyResult["master_keys"] + = Object.assign({}, ...(responses.map(res => res.master_keys || {}))); + const ssks: IDownloadKeyResult["self_signing_keys"] + = Object.assign({}, ...(responses.map(res => res.self_signing_keys || {}))); + const usks: IDownloadKeyResult["user_signing_keys"] + = Object.assign({}, ...(responses.map(res => res.user_signing_keys || {}))); // yield to other things that want to execute in between users, to // avoid wedging the CPU @@ -811,8 +815,12 @@ class DeviceListUpdateSerialiser { private async processQueryResponseForUser( userId: string, - dkResponse: object, - crossSigningResponse: any, // TODO types + dkResponse: IDownloadKeyResult["device_keys"]["user_id"], + crossSigningResponse: { + master: IDownloadKeyResult["master_keys"]["user_id"]; + self_signing: IDownloadKeyResult["master_keys"]["user_id"]; // eslint-disable-line camelcase + user_signing: IDownloadKeyResult["user_signing_keys"]["user_id"]; // eslint-disable-line camelcase + }, ): Promise { logger.log('got device keys for ' + userId + ':', dkResponse); logger.log('got cross-signing keys for ' + userId + ':', crossSigningResponse); @@ -869,7 +877,7 @@ async function updateStoredDeviceKeysForUser( olmDevice: OlmDevice, userId: string, userStore: Record, - userResult: object, + userResult: IDownloadKeyResult["device_keys"]["user_id"], localUserId: string, localDeviceId: string, ): Promise { diff --git a/src/crypto/backup.ts b/src/crypto/backup.ts index 161e7b40f..295b48e9f 100644 --- a/src/crypto/backup.ts +++ b/src/crypto/backup.ts @@ -33,6 +33,7 @@ import { encryptAES, decryptAES, calculateKeyCheck } from './aes'; import { getCrypto } from '../utils'; import { ICurve25519AuthData, IAes256AuthData, IKeyBackupInfo, IKeyBackupSession } from "./keybackup"; import { UnstableValue } from "../NamespacedValue"; +import { IMegolmSessionData } from "./index"; const KEY_BACKUP_KEYS_PER_REQUEST = 200; @@ -87,7 +88,7 @@ interface BackupAlgorithmClass { interface BackupAlgorithm { untrusted: boolean; encryptSession(data: Record): Promise; - decryptSessions(ciphertexts: Record): Promise[]>; + decryptSessions(ciphertexts: Record): Promise; authData: AuthData; keyMatches(key: ArrayLike): Promise; free(): void; @@ -300,7 +301,7 @@ export class BackupManager { const ret = { usable: false, trusted_locally: false, - sigs: [], + sigs: [] as SigInfo[], }; if ( @@ -320,7 +321,7 @@ export class BackupManager { ret.trusted_locally = true; } - const mySigs = backupInfo.auth_data.signatures[this.baseApis.getUserId()] || []; + const mySigs = backupInfo.auth_data.signatures[this.baseApis.getUserId()] || {}; for (const keyId of Object.keys(mySigs)) { const keyIdParts = keyId.split(':'); @@ -645,9 +646,7 @@ export class Curve25519 implements BackupAlgorithm { return this.publicKey.encrypt(JSON.stringify(plainText)); } - public async decryptSessions( - sessions: Record, - ): Promise[]> { + public async decryptSessions(sessions: Record): Promise { const privKey = await this.getKey(); const decryption = new global.Olm.PkDecryption(); try { @@ -658,7 +657,7 @@ export class Curve25519 implements BackupAlgorithm { throw { errcode: MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY }; } - const keys = []; + const keys: IMegolmSessionData[] = []; for (const [sessionId, sessionData] of Object.entries(sessions)) { try { @@ -777,8 +776,8 @@ export class Aes256 implements BackupAlgorithm { return await encryptAES(JSON.stringify(plainText), this.key, data.session_id); } - async decryptSessions(sessions: Record): Promise[]> { - const keys = []; + async decryptSessions(sessions: Record): Promise { + const keys: IMegolmSessionData[] = []; for (const [sessionId, sessionData] of Object.entries(sessions)) { try { diff --git a/src/crypto/dehydration.ts b/src/crypto/dehydration.ts index c9a9211fa..652f72e3e 100644 --- a/src/crypto/dehydration.ts +++ b/src/crypto/dehydration.ts @@ -22,9 +22,7 @@ import { decryptAES, encryptAES } from './aes'; import { logger } from '../logger'; import { ISecretStorageKeyInfo } from "./api"; import { Crypto } from "./index"; - -// FIXME: these types should eventually go in a different file -type Signatures = Record>; +import { ISignatures } from "../@types/signed"; export interface IDehydratedDevice { device_id: string; // eslint-disable-line camelcase @@ -43,13 +41,13 @@ export interface IDeviceKeys { device_id: string; // eslint-disable-line camelcase user_id: string; // eslint-disable-line camelcase keys: Record; - signatures?: Signatures; + signatures?: ISignatures; } export interface IOneTimeKey { key: string; fallback?: boolean; - signatures?: Signatures; + signatures?: ISignatures; } export const DEHYDRATION_ALGORITHM = "org.matrix.msc2697.v1.olm.libolm_pickle"; @@ -244,7 +242,7 @@ export class DehydrationManager { } logger.log("Preparing one-time keys"); - const oneTimeKeys = {}; + const oneTimeKeys: Record = {}; for (const [keyId, key] of Object.entries(otks.curve25519)) { const k: IOneTimeKey = { key }; const signature = account.sign(anotherjson.stringify(k)); diff --git a/src/crypto/index.ts b/src/crypto/index.ts index 6b71ed8ec..85ba4de45 100644 --- a/src/crypto/index.ts +++ b/src/crypto/index.ts @@ -179,6 +179,13 @@ export interface IEventDecryptionResult { untrusted?: boolean; } +export interface IRequestsMap { + getRequest(event: MatrixEvent): VerificationRequest; + getRequestByChannel(channel: IVerificationChannel): VerificationRequest; + setRequest(event: MatrixEvent, request: VerificationRequest): void; + setRequestByChannel(channel: IVerificationChannel, request: VerificationRequest): void; +} + export class Crypto extends EventEmitter { /** * @return {string} The version of Olm. @@ -808,7 +815,7 @@ export class Crypto extends EventEmitter { } }; - const signKeyBackupWithCrossSigning = async (keyBackupAuthData) => { + const signKeyBackupWithCrossSigning = async (keyBackupAuthData: IKeyBackupInfo["auth_data"]) => { if ( this.crossSigningInfo.getId() && await this.crossSigningInfo.isStoredInKeyCache("master") @@ -1148,7 +1155,7 @@ export class Crypto extends EventEmitter { const signedDevice = await this.crossSigningInfo.signDevice(this.userId, device); logger.info(`Starting background key sig upload for ${this.deviceId}`); - const upload = ({ shouldEmit }) => { + const upload = ({ shouldEmit = false }) => { return this.baseApis.uploadKeySignatures({ [this.userId]: { [this.deviceId]: signedDevice, @@ -1184,7 +1191,7 @@ export class Crypto extends EventEmitter { // Check all users for signatures if upgrade callback present // FIXME: do this in batches - const users = {}; + const users: Record = {}; for (const [userId, crossSigningInfo] of Object.entries(this.deviceList.crossSigningInfo)) { const upgradeInfo = await this.checkForDeviceVerificationUpgrade( @@ -1482,7 +1489,7 @@ export class Crypto extends EventEmitter { !crossSigningPrivateKeys.has("user_signing") ); - const keySignatures = {}; + const keySignatures: Record = {}; if (selfSigningChanged) { logger.info("Got new self-signing key", newCrossSigning.getId("self_signing")); @@ -1537,7 +1544,7 @@ export class Crypto extends EventEmitter { // We may have existing signatures from deleted devices, which will cause // the entire upload to fail. keySignatures[this.crossSigningInfo.getId()] = Object.assign( - {}, + {} as ISignedKey, masterKey, { signatures: { @@ -1551,7 +1558,7 @@ export class Crypto extends EventEmitter { const keysToUpload = Object.keys(keySignatures); if (keysToUpload.length) { - const upload = ({ shouldEmit }) => { + const upload = ({ shouldEmit = false }) => { logger.info(`Starting background key sig upload for ${keysToUpload}`); return this.baseApis.uploadKeySignatures({ [this.userId]: keySignatures }) .then((response) => { @@ -1927,7 +1934,7 @@ export class Crypto extends EventEmitter { } const oneTimeKeys = await this.olmDevice.getOneTimeKeys(); - const oneTimeJson = {}; + const oneTimeJson: Record = {}; for (const keyId in oneTimeKeys.curve25519) { if (oneTimeKeys.curve25519.hasOwnProperty(keyId)) { @@ -2076,7 +2083,7 @@ export class Crypto extends EventEmitter { ); const device = await this.crossSigningInfo.signUser(xsk); if (device) { - const upload = async ({ shouldEmit }) => { + const upload = async ({ shouldEmit = false }) => { logger.info("Uploading signature for " + userId + "..."); const response = await this.baseApis.uploadKeySignatures({ [userId]: { @@ -2149,7 +2156,7 @@ export class Crypto extends EventEmitter { logger.info("Own device " + deviceId + " marked verified: signing"); // Signing only needed if other device not already signed - let device; + let device: ISignedKey; const deviceTrust = this.checkDeviceTrust(userId, deviceId); if (deviceTrust.isCrossSigningVerified()) { logger.log(`Own device ${deviceId} already cross-signing verified`); @@ -2160,7 +2167,7 @@ export class Crypto extends EventEmitter { } if (device) { - const upload = async ({ shouldEmit }) => { + const upload = async ({ shouldEmit = false }) => { logger.info("Uploading signature for " + deviceId); const response = await this.baseApis.uploadKeySignatures({ [userId]: { @@ -2204,11 +2211,7 @@ export class Crypto extends EventEmitter { return Promise.resolve(existingRequest); } const channel = new InRoomChannel(this.baseApis, roomId, userId); - return this.requestVerificationWithChannel( - userId, - channel, - this.inRoomVerificationRequests, - ); + return this.requestVerificationWithChannel(userId, channel, this.inRoomVerificationRequests); } public requestVerification(userId: string, devices: string[]): Promise { @@ -2220,17 +2223,13 @@ export class Crypto extends EventEmitter { return Promise.resolve(existingRequest); } const channel = new ToDeviceChannel(this.baseApis, userId, devices, ToDeviceChannel.makeTransactionId()); - return this.requestVerificationWithChannel( - userId, - channel, - this.toDeviceVerificationRequests, - ); + return this.requestVerificationWithChannel(userId, channel, this.toDeviceVerificationRequests); } private async requestVerificationWithChannel( userId: string, channel: IVerificationChannel, - requestsMap: any, // TODO types + requestsMap: IRequestsMap, ): Promise { let request = new VerificationRequest(channel, this.verificationMethods, this.baseApis); // if transaction id is already known, add request @@ -2618,7 +2617,7 @@ export class Crypto extends EventEmitter { users: string[], force?: boolean, ): Promise>> { - const devicesByUser = {}; + const devicesByUser: Record = {}; for (let i = 0; i < users.length; ++i) { const userId = users[i]; @@ -2651,7 +2650,7 @@ export class Crypto extends EventEmitter { * @return {module:crypto/OlmDevice.MegolmSessionData[]} a list of session export objects */ public async exportRoomKeys(): Promise { - const exportedSessions = []; + const exportedSessions: IMegolmSessionData[] = []; await this.cryptoStore.doTxn( 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { this.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (s) => { @@ -2783,8 +2782,7 @@ export class Crypto extends EventEmitter { delete content['io.element.performance_metrics']; } - const encryptedContent = await alg.encryptMessage( - room, event.getType(), content); + const encryptedContent = await alg.encryptMessage(room, event.getType(), content); if (mRelatesTo) { encryptedContent['m.relates_to'] = mRelatesTo; @@ -3154,7 +3152,7 @@ export class Crypto extends EventEmitter { if (!ToDeviceChannel.validateEvent(event, this.baseApis)) { return; } - const createRequest = event => { + const createRequest = (event: MatrixEvent) => { if (!ToDeviceChannel.canCreateRequest(ToDeviceChannel.getEventType(event))) { return; } @@ -3172,11 +3170,7 @@ export class Crypto extends EventEmitter { return new VerificationRequest( channel, this.verificationMethods, this.baseApis); }; - this.handleVerificationEvent( - event, - this.toDeviceVerificationRequests, - createRequest, - ); + this.handleVerificationEvent(event, this.toDeviceVerificationRequests, createRequest); } /** @@ -3199,7 +3193,7 @@ export class Crypto extends EventEmitter { if (!InRoomChannel.validateEvent(event, this.baseApis)) { return; } - const createRequest = event => { + const createRequest = (event: MatrixEvent) => { const channel = new InRoomChannel( this.baseApis, event.getRoomId(), @@ -3207,18 +3201,13 @@ export class Crypto extends EventEmitter { return new VerificationRequest( channel, this.verificationMethods, this.baseApis); }; - this.handleVerificationEvent( - event, - this.inRoomVerificationRequests, - createRequest, - liveEvent, - ); + this.handleVerificationEvent(event, this.inRoomVerificationRequests, createRequest, liveEvent); }; private async handleVerificationEvent( event: MatrixEvent, - requestsMap: any, // TODO types - createRequest: any, // TODO types + requestsMap: IRequestsMap, + createRequest: (event: MatrixEvent) => VerificationRequest, isLiveEvent = true, ): Promise { // Wait for event to get its final ID with pendingEventOrdering: "chronological", since DM channels depend on it. @@ -3332,7 +3321,7 @@ export class Crypto extends EventEmitter { return; } } - const devicesByUser = {}; + const devicesByUser: Record = {}; devicesByUser[sender] = [device]; await olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser, true); diff --git a/src/crypto/olmlib.ts b/src/crypto/olmlib.ts index fe90607d9..19c34fe2b 100644 --- a/src/crypto/olmlib.ts +++ b/src/crypto/olmlib.ts @@ -28,7 +28,8 @@ import { OlmDevice } from "./OlmDevice"; import { DeviceInfo } from "./deviceinfo"; import { logger } from '../logger'; import { IOneTimeKey } from "./dehydration"; -import { MatrixClient } from "../client"; +import { IClaimOTKsResult, MatrixClient } from "../client"; +import { ISignatures } from "../@types/signed"; enum Algorithm { Olm = "m.olm.v1.curve25519-aes-sha2", @@ -132,6 +133,11 @@ export async function encryptMessageForDevice( ); } +interface IExistingOlmSession { + device: DeviceInfo; + sessionId?: string; +} + /** * Get the existing olm sessions for the given devices, and the devices that * don't have olm sessions. @@ -152,11 +158,11 @@ export async function getExistingOlmSessions( olmDevice: OlmDevice, baseApis: MatrixClient, devicesByUser: Record, -) { - const devicesWithoutSession = {}; - const sessions = {}; +): Promise<[Record, Record>]> { + const devicesWithoutSession: {[userId: string]: DeviceInfo[]} = {}; + const sessions: {[userId: string]: {[deviceId: string]: IExistingOlmSession}} = {}; - const promises = []; + const promises: Promise[] = []; for (const [userId, devices] of Object.entries(devicesByUser)) { for (const deviceInfo of devices) { @@ -230,10 +236,10 @@ export async function ensureOlmSessionsForDevices( force = false; } - const devicesWithoutSession = [ + const devicesWithoutSession: [string, string][] = [ // [userId, deviceId], ... ]; - const result = {}; + const result: {[userId: string]: {[deviceId: string]: IExistingOlmSession}} = {}; const resolveSession: Record void> = {}; // Mark all sessions this task intends to update as in progress. It is @@ -321,9 +327,7 @@ export async function ensureOlmSessionsForDevices( let taskDetail = `one-time keys for ${devicesWithoutSession.length} devices`; try { log.debug(`Claiming ${taskDetail}`); - res = await baseApis.claimOneTimeKeys( - devicesWithoutSession, oneTimeKeyAlgorithm, otkTimeout, - ); + res = await baseApis.claimOneTimeKeys(devicesWithoutSession, oneTimeKeyAlgorithm, otkTimeout); log.debug(`Claimed ${taskDetail}`); } catch (e) { for (const resolver of Object.values(resolveSession)) { @@ -337,8 +341,8 @@ export async function ensureOlmSessionsForDevices( failedServers.push(...Object.keys(res.failures)); } - const otkResult = res.one_time_keys || {}; - const promises = []; + const otkResult = res.one_time_keys || {} as IClaimOTKsResult["one_time_keys"]; + const promises: Promise[] = []; for (const [userId, devices] of Object.entries(devicesByUser)) { const userRes = otkResult[userId] || {}; for (let j = 0; j < devices.length; j++) { @@ -359,7 +363,7 @@ export async function ensureOlmSessionsForDevices( } const deviceRes = userRes[deviceId] || {}; - let oneTimeKey = null; + let oneTimeKey: IOneTimeKey = null; for (const keyId in deviceRes) { if (keyId.indexOf(oneTimeKeyAlgorithm + ":") === 0) { oneTimeKey = deviceRes[keyId]; @@ -441,7 +445,7 @@ async function _verifyKeyAndStartSession( export interface IObject { unsigned?: object; - signatures?: object; + signatures?: ISignatures; } /** diff --git a/src/crypto/store/indexeddb-crypto-store-backend.ts b/src/crypto/store/indexeddb-crypto-store-backend.ts index 3f25a0353..8e311a1a2 100644 --- a/src/crypto/store/indexeddb-crypto-store-backend.ts +++ b/src/crypto/store/indexeddb-crypto-store-backend.ts @@ -200,10 +200,10 @@ export class Backend implements CryptoStore { // index into the wantedStates array let stateIndex = 0; - let result; + let result: OutgoingRoomKeyRequest; - function onsuccess(ev) { - const cursor = ev.target.result; + function onsuccess(this: IDBRequest) { + const cursor = this.result; if (cursor) { // got a match result = cursor.value; @@ -218,7 +218,7 @@ export class Backend implements CryptoStore { } const wantedState = wantedStates[stateIndex]; - const cursorReq = ev.target.source.openCursor(wantedState); + const cursorReq = (this.source as IDBIndex).openCursor(wantedState); cursorReq.onsuccess = onsuccess; } @@ -255,10 +255,10 @@ export class Backend implements CryptoStore { wantedStates: number[], ): Promise { let stateIndex = 0; - const results = []; + const results: OutgoingRoomKeyRequest[] = []; - function onsuccess(ev) { - const cursor = ev.target.result; + function onsuccess(this: IDBRequest) { + const cursor = this.result; if (cursor) { const keyReq = cursor.value; if (keyReq.recipients.includes({ userId, deviceId })) { @@ -274,7 +274,7 @@ export class Backend implements CryptoStore { } const wantedState = wantedStates[stateIndex]; - const cursorReq = ev.target.source.openCursor(wantedState); + const cursorReq = (this.source as IDBIndex).openCursor(wantedState); cursorReq.onsuccess = onsuccess; } } @@ -306,10 +306,10 @@ export class Backend implements CryptoStore { expectedState: number, updates: Partial, ): Promise { - let result = null; + let result: OutgoingRoomKeyRequest = null; - function onsuccess(ev) { - const cursor = ev.target.result; + function onsuccess(this: IDBRequest) { + const cursor = this.result; if (!cursor) { return; } @@ -444,7 +444,7 @@ export class Backend implements CryptoStore { const objectStore = txn.objectStore("sessions"); const idx = objectStore.index("deviceKey"); const getReq = idx.openCursor(deviceKey); - const results = {}; + const results: Parameters[2]>[0] = {}; getReq.onsuccess = function() { const cursor = getReq.result; if (cursor) { @@ -734,7 +734,7 @@ export class Backend implements CryptoStore { } public getEndToEndRooms(txn: IDBTransaction, func: (rooms: Record) => void): void { - const rooms = {}; + const rooms: Parameters[1]>[0] = {}; const objectStore = txn.objectStore("rooms"); const getReq = objectStore.openCursor(); getReq.onsuccess = function() { @@ -756,7 +756,7 @@ export class Backend implements CryptoStore { public getSessionsNeedingBackup(limit: number): Promise { return new Promise((resolve, reject) => { - const sessions = []; + const sessions: ISession[] = []; const txn = this.db.transaction( ["sessions_needing_backup", "inbound_group_sessions"], @@ -877,8 +877,8 @@ export class Backend implements CryptoStore { func: (txn: IDBTransaction) => T, log: PrefixedLogger = logger, ): Promise { - let startTime; - let description; + let startTime: number; + let description: string; if (PROFILE_TRANSACTIONS) { const txnId = this.nextTxnId++; startTime = Date.now(); diff --git a/src/crypto/store/localStorage-crypto-store.ts b/src/crypto/store/localStorage-crypto-store.ts index 08f23affe..bd0b87bc4 100644 --- a/src/crypto/store/localStorage-crypto-store.ts +++ b/src/crypto/store/localStorage-crypto-store.ts @@ -175,8 +175,10 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore { } public async filterOutNotifiedErrorDevices(devices: IOlmDevice[]): Promise { - const notifiedErrorDevices = getJsonItem(this.store, KEY_NOTIFIED_ERROR_DEVICES) || {}; - const ret = []; + const notifiedErrorDevices = getJsonItem( + this.store, KEY_NOTIFIED_ERROR_DEVICES, + ) || {}; + const ret: IOlmDevice[] = []; for (const device of devices) { const { userId, deviceInfo } = device; @@ -291,7 +293,7 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore { } public getEndToEndRooms(txn: unknown, func: (rooms: Record) => void): void { - const result = {}; + const result: Record = {}; const prefix = keyEndToEndRoomsPrefix(''); for (let i = 0; i < this.store.length; ++i) { @@ -306,7 +308,7 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore { public getSessionsNeedingBackup(limit: number): Promise { const sessionsNeedingBackup = getJsonItem(this.store, KEY_SESSIONS_NEEDING_BACKUP) || {}; - const sessions = []; + const sessions: ISession[] = []; for (const session in sessionsNeedingBackup) { if (Object.prototype.hasOwnProperty.call(sessionsNeedingBackup, session)) { diff --git a/src/crypto/verification/request/Channel.ts b/src/crypto/verification/request/Channel.ts index 88955006b..3bba7d822 100644 --- a/src/crypto/verification/request/Channel.ts +++ b/src/crypto/verification/request/Channel.ts @@ -30,4 +30,5 @@ export interface IVerificationChannel { sendCompleted(type: string, content: Record): Promise; completedContentFromEvent(event: MatrixEvent): Record; canCreateRequest(type: string): boolean; + handleEvent(event: MatrixEvent, request: VerificationRequest, isLiveEvent: boolean): Promise; } diff --git a/src/crypto/verification/request/InRoomChannel.ts b/src/crypto/verification/request/InRoomChannel.ts index f07c39ffb..9e8690e1d 100644 --- a/src/crypto/verification/request/InRoomChannel.ts +++ b/src/crypto/verification/request/InRoomChannel.ts @@ -26,6 +26,7 @@ import { IVerificationChannel } from "./Channel"; import { EventType } from "../../../@types/event"; import { MatrixClient } from "../../../client"; import { MatrixEvent } from "../../../models/event"; +import { IRequestsMap } from "../.."; const MESSAGE_TYPE = EventType.RoomMessage; const M_REFERENCE = "m.reference"; @@ -304,7 +305,7 @@ export class InRoomChannel implements IVerificationChannel { } } -export class InRoomRequests { +export class InRoomRequests implements IRequestsMap { private requestsByRoomId = new Map>(); public getRequest(event: MatrixEvent): VerificationRequest { @@ -328,7 +329,7 @@ export class InRoomRequests { this.doSetRequest(event.getRoomId(), InRoomChannel.getTransactionId(event), request); } - public setRequestByChannel(channel: InRoomChannel, request: VerificationRequest): void { + public setRequestByChannel(channel: IVerificationChannel, request: VerificationRequest): void { this.doSetRequest(channel.roomId, channel.transactionId, request); } diff --git a/src/crypto/verification/request/ToDeviceChannel.ts b/src/crypto/verification/request/ToDeviceChannel.ts index 538a3db2c..61bd8bc34 100644 --- a/src/crypto/verification/request/ToDeviceChannel.ts +++ b/src/crypto/verification/request/ToDeviceChannel.ts @@ -30,8 +30,9 @@ import { errorFromEvent, newUnexpectedMessageError } from "../Error"; import { MatrixEvent } from "../../../models/event"; import { IVerificationChannel } from "./Channel"; import { MatrixClient } from "../../../client"; +import { IRequestsMap } from '../..'; -type Request = VerificationRequest; +export type Request = VerificationRequest; /** * A key verification channel that sends verification events over to_device messages. @@ -276,7 +277,7 @@ export class ToDeviceChannel implements IVerificationChannel { private async sendToDevices(type: string, content: Record, devices: string[]): Promise { if (devices.length) { - const msgMap = {}; + const msgMap: Record> = {}; for (const deviceId of devices) { msgMap[deviceId] = content; } @@ -294,7 +295,7 @@ export class ToDeviceChannel implements IVerificationChannel { } } -export class ToDeviceRequests { +export class ToDeviceRequests implements IRequestsMap { private requestsByUserId = new Map>(); public getRequest(event: MatrixEvent): Request { diff --git a/src/crypto/verification/request/VerificationRequest.ts b/src/crypto/verification/request/VerificationRequest.ts index 5d145508b..b6c0d9ef4 100644 --- a/src/crypto/verification/request/VerificationRequest.ts +++ b/src/crypto/verification/request/VerificationRequest.ts @@ -582,7 +582,9 @@ export class VerificationRequest(); this.commonMethods = content.methods.filter(m => this.verificationMethods.has(m)); } diff --git a/src/models/MSC3089TreeSpace.ts b/src/models/MSC3089TreeSpace.ts index 48209dbc0..d17b4b589 100644 --- a/src/models/MSC3089TreeSpace.ts +++ b/src/models/MSC3089TreeSpace.ts @@ -282,7 +282,7 @@ export class MSC3089TreeSpace { const members = this.room.currentState.getStateEvents(EventType.RoomMember); for (const member of members) { const isNotUs = member.getStateKey() !== this.client.getUserId(); - if (isNotUs && kickMemberships.includes(member.getContent()['membership'])) { + if (isNotUs && kickMemberships.includes(member.getContent().membership)) { const stateKey = member.getStateKey(); if (!stateKey) { throw new Error("State key not found for branch");