1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-07-31 15:24:23 +03:00

Iterate typing to work towards noImplicitAny (#2061)

This commit is contained in:
Michael Telatynski
2021-12-14 14:32:35 +00:00
committed by GitHub
parent aeec4aa4a8
commit 963c7690b6
16 changed files with 161 additions and 140 deletions

View File

@ -312,7 +312,7 @@ describe("MSC3089Branch", () => {
} as MatrixEvent); } as MatrixEvent);
const events = [await branch.getFileEvent(), await branch2.getFileEvent(), { const events = [await branch.getFileEvent(), await branch2.getFileEvent(), {
replacingEventId: () => null, replacingEventId: (): string => null,
getId: () => "$unknown", getId: () => "$unknown",
}]; }];
staticRoom.getLiveTimeline = () => ({ getEvents: () => events }) as EventTimeline; staticRoom.getLiveTimeline = () => ({ getEvents: () => events }) as EventTimeline;

View File

@ -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 DummyInterfaceWeShouldntBeUsingThis {}
interface Navigator { interface Navigator {

View File

@ -621,7 +621,7 @@ export interface IMyDevice {
last_seen_ts?: number; last_seen_ts?: number;
} }
interface IDownloadKeyResult { export interface IDownloadKeyResult {
failures: { [serverName: string]: object }; failures: { [serverName: string]: object };
device_keys: { device_keys: {
[userId: string]: { [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 }; failures: { [serverName: string]: object };
one_time_keys: { one_time_keys: {
[userId: string]: { [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( return this.http.authedRequest(
undefined, "GET", "/capabilities", undefined, "GET", "/capabilities",
).catch((e: Error) => { ).catch((e: Error): void => {
// We swallow errors because we need a default object anyhow
logger.error(e); logger.error(e);
return null; // otherwise consume the error }).then((r: { capabilities?: ICapabilities } = {}) => {
}).then((r) => {
if (!r) r = {};
const capabilities: ICapabilities = r["capabilities"] || {}; const capabilities: ICapabilities = r["capabilities"] || {};
// If the capabilities missed the cache, cache it for a shorter amount // 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 totalKeyCount = 0;
let keys = []; let keys: IMegolmSessionData[] = [];
const path = this.makeKeyBackupPath(targetRoomId, targetSessionId, backupInfo.version); const path = this.makeKeyBackupPath(targetRoomId, targetSessionId, backupInfo.version);
@ -5692,18 +5719,14 @@ export class MatrixClient extends EventEmitter {
searchResults.count = roomEvents.count; searchResults.count = roomEvents.count;
searchResults.next_batch = roomEvents.next_batch; searchResults.next_batch = roomEvents.next_batch;
// combine the highlight list with our existing list; build an object // combine the highlight list with our existing list;
// to avoid O(N^2) fail const highlights = new Set<string>(roomEvents.highlights);
const highlights = {};
roomEvents.highlights.forEach((hl) => {
highlights[hl] = 1;
});
searchResults.highlights.forEach((hl) => { searchResults.highlights.forEach((hl) => {
highlights[hl] = 1; highlights.add(hl);
}); });
// turn it back into a list. // turn it back into a list.
searchResults.highlights = Object.keys(highlights); searchResults.highlights = Array.from(highlights);
// append the new results to our existing results // append the new results to our existing results
const resultsLength = roomEvents.results ? roomEvents.results.length : 0; const resultsLength = roomEvents.results ? roomEvents.results.length : 0;
@ -5794,11 +5817,9 @@ export class MatrixClient extends EventEmitter {
return this.http.authedRequest( return this.http.authedRequest(
undefined, "GET", path, undefined, undefined, undefined, "GET", path, undefined, undefined,
).then((response) => { ).then((response: IFilterDefinition) => {
// persist the filter // persist the filter
const filter = Filter.fromJson( const filter = Filter.fromJson(userId, filterId, response);
userId, filterId, response,
);
this.store.storeFilter(filter); this.store.storeFilter(filter);
return filter; return filter;
}); });
@ -6096,7 +6117,7 @@ export class MatrixClient extends EventEmitter {
{ {
prefix: '', prefix: '',
}, },
).catch((e) => { ).catch((e: Error) => {
// Need to unset this if it fails, otherwise we'll never retry // Need to unset this if it fails, otherwise we'll never retry
this.serverVersionsPromise = null; this.serverVersionsPromise = null;
// but rethrow the exception to anything that was waiting // but rethrow the exception to anything that was waiting
@ -6420,7 +6441,7 @@ export class MatrixClient extends EventEmitter {
public isUsernameAvailable(username: string): Promise<true> { public isUsernameAvailable(username: string): Promise<true> {
return this.http.authedRequest( return this.http.authedRequest(
undefined, "GET", '/register/available', { username: username }, undefined, "GET", '/register/available', { username: username },
).then((response) => { ).then((response: { available: boolean }) => {
return response.available; return response.available;
}); });
} }
@ -7741,11 +7762,11 @@ export class MatrixClient extends EventEmitter {
* an error response ({@link module:http-api.MatrixError}). * an error response ({@link module:http-api.MatrixError}).
*/ */
public claimOneTimeKeys( public claimOneTimeKeys(
devices: string[], devices: [string, string][],
keyAlgorithm = "signed_curve25519", keyAlgorithm = "signed_curve25519",
timeout?: number, timeout?: number,
): Promise<IClaimOTKsResult> { ): Promise<IClaimOTKsResult> {
const queries = {}; const queries: Record<string, Record<string, string>> = {};
if (keyAlgorithm === undefined) { if (keyAlgorithm === undefined) {
keyAlgorithm = "signed_curve25519"; keyAlgorithm = "signed_curve25519";

View File

@ -33,6 +33,7 @@ import { OlmDevice } from "./OlmDevice";
import { ICryptoCallbacks } from "../matrix"; import { ICryptoCallbacks } from "../matrix";
import { ISignatures } from "../@types/signed"; import { ISignatures } from "../@types/signed";
import { CryptoStore } from "./store/base"; import { CryptoStore } from "./store/base";
import { ISecretStorageKeyInfo } from "./api";
const KEY_REQUEST_TIMEOUT_MS = 1000 * 60; 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) // check what SSSS keys have encrypted the master key (if any)
const stored = await secretStorage.isStored("m.cross_signing.master", false) || {}; const stored = await secretStorage.isStored("m.cross_signing.master", false) || {};
// then check which of those SSSS keys have also encrypted the SSK and USK // then check which of those SSSS keys have also encrypted the SSK and USK
function intersect(s) { function intersect(s: Record<string, ISecretStorageKeyInfo>) {
for (const k of Object.keys(stored)) { for (const k of Object.keys(stored)) {
if (!s[k]) { if (!s[k]) {
delete stored[k]; delete stored[k];

View File

@ -28,7 +28,7 @@ import { CrossSigningInfo, ICrossSigningInfo } from './CrossSigning';
import * as olmlib from './olmlib'; import * as olmlib from './olmlib';
import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store'; import { IndexedDBCryptoStore } from './store/indexeddb-crypto-store';
import { chunkPromises, defer, IDeferred, sleep } from '../utils'; import { chunkPromises, defer, IDeferred, sleep } from '../utils';
import { MatrixClient } from "../client"; import { IDownloadKeyResult, MatrixClient } from "../client";
import { OlmDevice } from "./OlmDevice"; import { OlmDevice } from "./OlmDevice";
import { CryptoStore } from "./store/base"; import { CryptoStore } from "./store/base";
@ -756,17 +756,21 @@ class DeviceListUpdateSerialiser {
opts.token = this.syncToken; opts.token = this.syncToken;
} }
const factories = []; const factories: Array<() => Promise<IDownloadKeyResult>> = [];
for (let i = 0; i < downloadUsers.length; i += this.deviceList.keyDownloadChunkSize) { for (let i = 0; i < downloadUsers.length; i += this.deviceList.keyDownloadChunkSize) {
const userSlice = downloadUsers.slice(i, i + this.deviceList.keyDownloadChunkSize); const userSlice = downloadUsers.slice(i, i + this.deviceList.keyDownloadChunkSize);
factories.push(() => this.baseApis.downloadKeysForUsers(userSlice, opts)); factories.push(() => this.baseApis.downloadKeysForUsers(userSlice, opts));
} }
chunkPromises(factories, 3).then(async (responses: any[]) => { chunkPromises(factories, 3).then(async (responses: IDownloadKeyResult[]) => {
const dk = Object.assign({}, ...(responses.map(res => res.device_keys || {}))); const dk: IDownloadKeyResult["device_keys"]
const masterKeys = Object.assign({}, ...(responses.map(res => res.master_keys || {}))); = Object.assign({}, ...(responses.map(res => res.device_keys || {})));
const ssks = Object.assign({}, ...(responses.map(res => res.self_signing_keys || {}))); const masterKeys: IDownloadKeyResult["master_keys"]
const usks = Object.assign({}, ...(responses.map(res => res.user_signing_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 // yield to other things that want to execute in between users, to
// avoid wedging the CPU // avoid wedging the CPU
@ -811,8 +815,12 @@ class DeviceListUpdateSerialiser {
private async processQueryResponseForUser( private async processQueryResponseForUser(
userId: string, userId: string,
dkResponse: object, dkResponse: IDownloadKeyResult["device_keys"]["user_id"],
crossSigningResponse: any, // TODO types 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<void> { ): Promise<void> {
logger.log('got device keys for ' + userId + ':', dkResponse); logger.log('got device keys for ' + userId + ':', dkResponse);
logger.log('got cross-signing keys for ' + userId + ':', crossSigningResponse); logger.log('got cross-signing keys for ' + userId + ':', crossSigningResponse);
@ -869,7 +877,7 @@ async function updateStoredDeviceKeysForUser(
olmDevice: OlmDevice, olmDevice: OlmDevice,
userId: string, userId: string,
userStore: Record<string, DeviceInfo>, userStore: Record<string, DeviceInfo>,
userResult: object, userResult: IDownloadKeyResult["device_keys"]["user_id"],
localUserId: string, localUserId: string,
localDeviceId: string, localDeviceId: string,
): Promise<boolean> { ): Promise<boolean> {

View File

@ -33,6 +33,7 @@ import { encryptAES, decryptAES, calculateKeyCheck } from './aes';
import { getCrypto } from '../utils'; import { getCrypto } from '../utils';
import { ICurve25519AuthData, IAes256AuthData, IKeyBackupInfo, IKeyBackupSession } from "./keybackup"; import { ICurve25519AuthData, IAes256AuthData, IKeyBackupInfo, IKeyBackupSession } from "./keybackup";
import { UnstableValue } from "../NamespacedValue"; import { UnstableValue } from "../NamespacedValue";
import { IMegolmSessionData } from "./index";
const KEY_BACKUP_KEYS_PER_REQUEST = 200; const KEY_BACKUP_KEYS_PER_REQUEST = 200;
@ -87,7 +88,7 @@ interface BackupAlgorithmClass {
interface BackupAlgorithm { interface BackupAlgorithm {
untrusted: boolean; untrusted: boolean;
encryptSession(data: Record<string, any>): Promise<any>; encryptSession(data: Record<string, any>): Promise<any>;
decryptSessions(ciphertexts: Record<string, IKeyBackupSession>): Promise<Record<string, any>[]>; decryptSessions(ciphertexts: Record<string, IKeyBackupSession>): Promise<IMegolmSessionData[]>;
authData: AuthData; authData: AuthData;
keyMatches(key: ArrayLike<number>): Promise<boolean>; keyMatches(key: ArrayLike<number>): Promise<boolean>;
free(): void; free(): void;
@ -300,7 +301,7 @@ export class BackupManager {
const ret = { const ret = {
usable: false, usable: false,
trusted_locally: false, trusted_locally: false,
sigs: [], sigs: [] as SigInfo[],
}; };
if ( if (
@ -320,7 +321,7 @@ export class BackupManager {
ret.trusted_locally = true; 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)) { for (const keyId of Object.keys(mySigs)) {
const keyIdParts = keyId.split(':'); const keyIdParts = keyId.split(':');
@ -645,9 +646,7 @@ export class Curve25519 implements BackupAlgorithm {
return this.publicKey.encrypt(JSON.stringify(plainText)); return this.publicKey.encrypt(JSON.stringify(plainText));
} }
public async decryptSessions( public async decryptSessions(sessions: Record<string, IKeyBackupSession>): Promise<IMegolmSessionData[]> {
sessions: Record<string, IKeyBackupSession>,
): Promise<Record<string, any>[]> {
const privKey = await this.getKey(); const privKey = await this.getKey();
const decryption = new global.Olm.PkDecryption(); const decryption = new global.Olm.PkDecryption();
try { try {
@ -658,7 +657,7 @@ export class Curve25519 implements BackupAlgorithm {
throw { errcode: MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY }; throw { errcode: MatrixClient.RESTORE_BACKUP_ERROR_BAD_KEY };
} }
const keys = []; const keys: IMegolmSessionData[] = [];
for (const [sessionId, sessionData] of Object.entries(sessions)) { for (const [sessionId, sessionData] of Object.entries(sessions)) {
try { try {
@ -777,8 +776,8 @@ export class Aes256 implements BackupAlgorithm {
return await encryptAES(JSON.stringify(plainText), this.key, data.session_id); return await encryptAES(JSON.stringify(plainText), this.key, data.session_id);
} }
async decryptSessions(sessions: Record<string, IKeyBackupSession>): Promise<Record<string, any>[]> { async decryptSessions(sessions: Record<string, IKeyBackupSession>): Promise<IMegolmSessionData[]> {
const keys = []; const keys: IMegolmSessionData[] = [];
for (const [sessionId, sessionData] of Object.entries(sessions)) { for (const [sessionId, sessionData] of Object.entries(sessions)) {
try { try {

View File

@ -22,9 +22,7 @@ import { decryptAES, encryptAES } from './aes';
import { logger } from '../logger'; import { logger } from '../logger';
import { ISecretStorageKeyInfo } from "./api"; import { ISecretStorageKeyInfo } from "./api";
import { Crypto } from "./index"; import { Crypto } from "./index";
import { ISignatures } from "../@types/signed";
// FIXME: these types should eventually go in a different file
type Signatures = Record<string, Record<string, string>>;
export interface IDehydratedDevice { export interface IDehydratedDevice {
device_id: string; // eslint-disable-line camelcase device_id: string; // eslint-disable-line camelcase
@ -43,13 +41,13 @@ export interface IDeviceKeys {
device_id: string; // eslint-disable-line camelcase device_id: string; // eslint-disable-line camelcase
user_id: string; // eslint-disable-line camelcase user_id: string; // eslint-disable-line camelcase
keys: Record<string, string>; keys: Record<string, string>;
signatures?: Signatures; signatures?: ISignatures;
} }
export interface IOneTimeKey { export interface IOneTimeKey {
key: string; key: string;
fallback?: boolean; fallback?: boolean;
signatures?: Signatures; signatures?: ISignatures;
} }
export const DEHYDRATION_ALGORITHM = "org.matrix.msc2697.v1.olm.libolm_pickle"; export const DEHYDRATION_ALGORITHM = "org.matrix.msc2697.v1.olm.libolm_pickle";
@ -244,7 +242,7 @@ export class DehydrationManager {
} }
logger.log("Preparing one-time keys"); logger.log("Preparing one-time keys");
const oneTimeKeys = {}; const oneTimeKeys: Record<string, IOneTimeKey> = {};
for (const [keyId, key] of Object.entries(otks.curve25519)) { for (const [keyId, key] of Object.entries(otks.curve25519)) {
const k: IOneTimeKey = { key }; const k: IOneTimeKey = { key };
const signature = account.sign(anotherjson.stringify(k)); const signature = account.sign(anotherjson.stringify(k));

View File

@ -179,6 +179,13 @@ export interface IEventDecryptionResult {
untrusted?: boolean; 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 { export class Crypto extends EventEmitter {
/** /**
* @return {string} The version of Olm. * @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 ( if (
this.crossSigningInfo.getId() && this.crossSigningInfo.getId() &&
await this.crossSigningInfo.isStoredInKeyCache("master") await this.crossSigningInfo.isStoredInKeyCache("master")
@ -1148,7 +1155,7 @@ export class Crypto extends EventEmitter {
const signedDevice = await this.crossSigningInfo.signDevice(this.userId, device); const signedDevice = await this.crossSigningInfo.signDevice(this.userId, device);
logger.info(`Starting background key sig upload for ${this.deviceId}`); logger.info(`Starting background key sig upload for ${this.deviceId}`);
const upload = ({ shouldEmit }) => { const upload = ({ shouldEmit = false }) => {
return this.baseApis.uploadKeySignatures({ return this.baseApis.uploadKeySignatures({
[this.userId]: { [this.userId]: {
[this.deviceId]: signedDevice, [this.deviceId]: signedDevice,
@ -1184,7 +1191,7 @@ export class Crypto extends EventEmitter {
// Check all users for signatures if upgrade callback present // Check all users for signatures if upgrade callback present
// FIXME: do this in batches // FIXME: do this in batches
const users = {}; const users: Record<string, IDeviceVerificationUpgrade> = {};
for (const [userId, crossSigningInfo] for (const [userId, crossSigningInfo]
of Object.entries(this.deviceList.crossSigningInfo)) { of Object.entries(this.deviceList.crossSigningInfo)) {
const upgradeInfo = await this.checkForDeviceVerificationUpgrade( const upgradeInfo = await this.checkForDeviceVerificationUpgrade(
@ -1482,7 +1489,7 @@ export class Crypto extends EventEmitter {
!crossSigningPrivateKeys.has("user_signing") !crossSigningPrivateKeys.has("user_signing")
); );
const keySignatures = {}; const keySignatures: Record<string, ISignedKey> = {};
if (selfSigningChanged) { if (selfSigningChanged) {
logger.info("Got new self-signing key", newCrossSigning.getId("self_signing")); 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 // We may have existing signatures from deleted devices, which will cause
// the entire upload to fail. // the entire upload to fail.
keySignatures[this.crossSigningInfo.getId()] = Object.assign( keySignatures[this.crossSigningInfo.getId()] = Object.assign(
{}, {} as ISignedKey,
masterKey, masterKey,
{ {
signatures: { signatures: {
@ -1551,7 +1558,7 @@ export class Crypto extends EventEmitter {
const keysToUpload = Object.keys(keySignatures); const keysToUpload = Object.keys(keySignatures);
if (keysToUpload.length) { if (keysToUpload.length) {
const upload = ({ shouldEmit }) => { const upload = ({ shouldEmit = false }) => {
logger.info(`Starting background key sig upload for ${keysToUpload}`); logger.info(`Starting background key sig upload for ${keysToUpload}`);
return this.baseApis.uploadKeySignatures({ [this.userId]: keySignatures }) return this.baseApis.uploadKeySignatures({ [this.userId]: keySignatures })
.then((response) => { .then((response) => {
@ -1927,7 +1934,7 @@ export class Crypto extends EventEmitter {
} }
const oneTimeKeys = await this.olmDevice.getOneTimeKeys(); const oneTimeKeys = await this.olmDevice.getOneTimeKeys();
const oneTimeJson = {}; const oneTimeJson: Record<string, { key: string }> = {};
for (const keyId in oneTimeKeys.curve25519) { for (const keyId in oneTimeKeys.curve25519) {
if (oneTimeKeys.curve25519.hasOwnProperty(keyId)) { if (oneTimeKeys.curve25519.hasOwnProperty(keyId)) {
@ -2076,7 +2083,7 @@ export class Crypto extends EventEmitter {
); );
const device = await this.crossSigningInfo.signUser(xsk); const device = await this.crossSigningInfo.signUser(xsk);
if (device) { if (device) {
const upload = async ({ shouldEmit }) => { const upload = async ({ shouldEmit = false }) => {
logger.info("Uploading signature for " + userId + "..."); logger.info("Uploading signature for " + userId + "...");
const response = await this.baseApis.uploadKeySignatures({ const response = await this.baseApis.uploadKeySignatures({
[userId]: { [userId]: {
@ -2149,7 +2156,7 @@ export class Crypto extends EventEmitter {
logger.info("Own device " + deviceId + " marked verified: signing"); logger.info("Own device " + deviceId + " marked verified: signing");
// Signing only needed if other device not already signed // Signing only needed if other device not already signed
let device; let device: ISignedKey;
const deviceTrust = this.checkDeviceTrust(userId, deviceId); const deviceTrust = this.checkDeviceTrust(userId, deviceId);
if (deviceTrust.isCrossSigningVerified()) { if (deviceTrust.isCrossSigningVerified()) {
logger.log(`Own device ${deviceId} already cross-signing verified`); logger.log(`Own device ${deviceId} already cross-signing verified`);
@ -2160,7 +2167,7 @@ export class Crypto extends EventEmitter {
} }
if (device) { if (device) {
const upload = async ({ shouldEmit }) => { const upload = async ({ shouldEmit = false }) => {
logger.info("Uploading signature for " + deviceId); logger.info("Uploading signature for " + deviceId);
const response = await this.baseApis.uploadKeySignatures({ const response = await this.baseApis.uploadKeySignatures({
[userId]: { [userId]: {
@ -2204,11 +2211,7 @@ export class Crypto extends EventEmitter {
return Promise.resolve(existingRequest); return Promise.resolve(existingRequest);
} }
const channel = new InRoomChannel(this.baseApis, roomId, userId); const channel = new InRoomChannel(this.baseApis, roomId, userId);
return this.requestVerificationWithChannel( return this.requestVerificationWithChannel(userId, channel, this.inRoomVerificationRequests);
userId,
channel,
this.inRoomVerificationRequests,
);
} }
public requestVerification(userId: string, devices: string[]): Promise<VerificationRequest> { public requestVerification(userId: string, devices: string[]): Promise<VerificationRequest> {
@ -2220,17 +2223,13 @@ export class Crypto extends EventEmitter {
return Promise.resolve(existingRequest); return Promise.resolve(existingRequest);
} }
const channel = new ToDeviceChannel(this.baseApis, userId, devices, ToDeviceChannel.makeTransactionId()); const channel = new ToDeviceChannel(this.baseApis, userId, devices, ToDeviceChannel.makeTransactionId());
return this.requestVerificationWithChannel( return this.requestVerificationWithChannel(userId, channel, this.toDeviceVerificationRequests);
userId,
channel,
this.toDeviceVerificationRequests,
);
} }
private async requestVerificationWithChannel( private async requestVerificationWithChannel(
userId: string, userId: string,
channel: IVerificationChannel, channel: IVerificationChannel,
requestsMap: any, // TODO types requestsMap: IRequestsMap,
): Promise<VerificationRequest> { ): Promise<VerificationRequest> {
let request = new VerificationRequest(channel, this.verificationMethods, this.baseApis); let request = new VerificationRequest(channel, this.verificationMethods, this.baseApis);
// if transaction id is already known, add request // if transaction id is already known, add request
@ -2618,7 +2617,7 @@ export class Crypto extends EventEmitter {
users: string[], users: string[],
force?: boolean, force?: boolean,
): Promise<Record<string, Record<string, olmlib.IOlmSessionResult>>> { ): Promise<Record<string, Record<string, olmlib.IOlmSessionResult>>> {
const devicesByUser = {}; const devicesByUser: Record<string, DeviceInfo[]> = {};
for (let i = 0; i < users.length; ++i) { for (let i = 0; i < users.length; ++i) {
const userId = users[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 * @return {module:crypto/OlmDevice.MegolmSessionData[]} a list of session export objects
*/ */
public async exportRoomKeys(): Promise<IMegolmSessionData[]> { public async exportRoomKeys(): Promise<IMegolmSessionData[]> {
const exportedSessions = []; const exportedSessions: IMegolmSessionData[] = [];
await this.cryptoStore.doTxn( await this.cryptoStore.doTxn(
'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => { 'readonly', [IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS], (txn) => {
this.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (s) => { this.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (s) => {
@ -2783,8 +2782,7 @@ export class Crypto extends EventEmitter {
delete content['io.element.performance_metrics']; delete content['io.element.performance_metrics'];
} }
const encryptedContent = await alg.encryptMessage( const encryptedContent = await alg.encryptMessage(room, event.getType(), content);
room, event.getType(), content);
if (mRelatesTo) { if (mRelatesTo) {
encryptedContent['m.relates_to'] = mRelatesTo; encryptedContent['m.relates_to'] = mRelatesTo;
@ -3154,7 +3152,7 @@ export class Crypto extends EventEmitter {
if (!ToDeviceChannel.validateEvent(event, this.baseApis)) { if (!ToDeviceChannel.validateEvent(event, this.baseApis)) {
return; return;
} }
const createRequest = event => { const createRequest = (event: MatrixEvent) => {
if (!ToDeviceChannel.canCreateRequest(ToDeviceChannel.getEventType(event))) { if (!ToDeviceChannel.canCreateRequest(ToDeviceChannel.getEventType(event))) {
return; return;
} }
@ -3172,11 +3170,7 @@ export class Crypto extends EventEmitter {
return new VerificationRequest( return new VerificationRequest(
channel, this.verificationMethods, this.baseApis); channel, this.verificationMethods, this.baseApis);
}; };
this.handleVerificationEvent( this.handleVerificationEvent(event, this.toDeviceVerificationRequests, createRequest);
event,
this.toDeviceVerificationRequests,
createRequest,
);
} }
/** /**
@ -3199,7 +3193,7 @@ export class Crypto extends EventEmitter {
if (!InRoomChannel.validateEvent(event, this.baseApis)) { if (!InRoomChannel.validateEvent(event, this.baseApis)) {
return; return;
} }
const createRequest = event => { const createRequest = (event: MatrixEvent) => {
const channel = new InRoomChannel( const channel = new InRoomChannel(
this.baseApis, this.baseApis,
event.getRoomId(), event.getRoomId(),
@ -3207,18 +3201,13 @@ export class Crypto extends EventEmitter {
return new VerificationRequest( return new VerificationRequest(
channel, this.verificationMethods, this.baseApis); channel, this.verificationMethods, this.baseApis);
}; };
this.handleVerificationEvent( this.handleVerificationEvent(event, this.inRoomVerificationRequests, createRequest, liveEvent);
event,
this.inRoomVerificationRequests,
createRequest,
liveEvent,
);
}; };
private async handleVerificationEvent( private async handleVerificationEvent(
event: MatrixEvent, event: MatrixEvent,
requestsMap: any, // TODO types requestsMap: IRequestsMap,
createRequest: any, // TODO types createRequest: (event: MatrixEvent) => VerificationRequest,
isLiveEvent = true, isLiveEvent = true,
): Promise<void> { ): Promise<void> {
// Wait for event to get its final ID with pendingEventOrdering: "chronological", since DM channels depend on it. // 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; return;
} }
} }
const devicesByUser = {}; const devicesByUser: Record<string, DeviceInfo[]> = {};
devicesByUser[sender] = [device]; devicesByUser[sender] = [device];
await olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser, true); await olmlib.ensureOlmSessionsForDevices(this.olmDevice, this.baseApis, devicesByUser, true);

View File

@ -28,7 +28,8 @@ import { OlmDevice } from "./OlmDevice";
import { DeviceInfo } from "./deviceinfo"; import { DeviceInfo } from "./deviceinfo";
import { logger } from '../logger'; import { logger } from '../logger';
import { IOneTimeKey } from "./dehydration"; import { IOneTimeKey } from "./dehydration";
import { MatrixClient } from "../client"; import { IClaimOTKsResult, MatrixClient } from "../client";
import { ISignatures } from "../@types/signed";
enum Algorithm { enum Algorithm {
Olm = "m.olm.v1.curve25519-aes-sha2", 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 * Get the existing olm sessions for the given devices, and the devices that
* don't have olm sessions. * don't have olm sessions.
@ -152,11 +158,11 @@ export async function getExistingOlmSessions(
olmDevice: OlmDevice, olmDevice: OlmDevice,
baseApis: MatrixClient, baseApis: MatrixClient,
devicesByUser: Record<string, DeviceInfo[]>, devicesByUser: Record<string, DeviceInfo[]>,
) { ): Promise<[Record<string, DeviceInfo[]>, Record<string, Record<string, IExistingOlmSession>>]> {
const devicesWithoutSession = {}; const devicesWithoutSession: {[userId: string]: DeviceInfo[]} = {};
const sessions = {}; const sessions: {[userId: string]: {[deviceId: string]: IExistingOlmSession}} = {};
const promises = []; const promises: Promise<void>[] = [];
for (const [userId, devices] of Object.entries(devicesByUser)) { for (const [userId, devices] of Object.entries(devicesByUser)) {
for (const deviceInfo of devices) { for (const deviceInfo of devices) {
@ -230,10 +236,10 @@ export async function ensureOlmSessionsForDevices(
force = false; force = false;
} }
const devicesWithoutSession = [ const devicesWithoutSession: [string, string][] = [
// [userId, deviceId], ... // [userId, deviceId], ...
]; ];
const result = {}; const result: {[userId: string]: {[deviceId: string]: IExistingOlmSession}} = {};
const resolveSession: Record<string, (sessionId?: string) => void> = {}; const resolveSession: Record<string, (sessionId?: string) => void> = {};
// Mark all sessions this task intends to update as in progress. It is // 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`; let taskDetail = `one-time keys for ${devicesWithoutSession.length} devices`;
try { try {
log.debug(`Claiming ${taskDetail}`); log.debug(`Claiming ${taskDetail}`);
res = await baseApis.claimOneTimeKeys( res = await baseApis.claimOneTimeKeys(devicesWithoutSession, oneTimeKeyAlgorithm, otkTimeout);
devicesWithoutSession, oneTimeKeyAlgorithm, otkTimeout,
);
log.debug(`Claimed ${taskDetail}`); log.debug(`Claimed ${taskDetail}`);
} catch (e) { } catch (e) {
for (const resolver of Object.values(resolveSession)) { for (const resolver of Object.values(resolveSession)) {
@ -337,8 +341,8 @@ export async function ensureOlmSessionsForDevices(
failedServers.push(...Object.keys(res.failures)); failedServers.push(...Object.keys(res.failures));
} }
const otkResult = res.one_time_keys || {}; const otkResult = res.one_time_keys || {} as IClaimOTKsResult["one_time_keys"];
const promises = []; const promises: Promise<void>[] = [];
for (const [userId, devices] of Object.entries(devicesByUser)) { for (const [userId, devices] of Object.entries(devicesByUser)) {
const userRes = otkResult[userId] || {}; const userRes = otkResult[userId] || {};
for (let j = 0; j < devices.length; j++) { for (let j = 0; j < devices.length; j++) {
@ -359,7 +363,7 @@ export async function ensureOlmSessionsForDevices(
} }
const deviceRes = userRes[deviceId] || {}; const deviceRes = userRes[deviceId] || {};
let oneTimeKey = null; let oneTimeKey: IOneTimeKey = null;
for (const keyId in deviceRes) { for (const keyId in deviceRes) {
if (keyId.indexOf(oneTimeKeyAlgorithm + ":") === 0) { if (keyId.indexOf(oneTimeKeyAlgorithm + ":") === 0) {
oneTimeKey = deviceRes[keyId]; oneTimeKey = deviceRes[keyId];
@ -441,7 +445,7 @@ async function _verifyKeyAndStartSession(
export interface IObject { export interface IObject {
unsigned?: object; unsigned?: object;
signatures?: object; signatures?: ISignatures;
} }
/** /**

View File

@ -200,10 +200,10 @@ export class Backend implements CryptoStore {
// index into the wantedStates array // index into the wantedStates array
let stateIndex = 0; let stateIndex = 0;
let result; let result: OutgoingRoomKeyRequest;
function onsuccess(ev) { function onsuccess(this: IDBRequest<IDBCursorWithValue>) {
const cursor = ev.target.result; const cursor = this.result;
if (cursor) { if (cursor) {
// got a match // got a match
result = cursor.value; result = cursor.value;
@ -218,7 +218,7 @@ export class Backend implements CryptoStore {
} }
const wantedState = wantedStates[stateIndex]; const wantedState = wantedStates[stateIndex];
const cursorReq = ev.target.source.openCursor(wantedState); const cursorReq = (this.source as IDBIndex).openCursor(wantedState);
cursorReq.onsuccess = onsuccess; cursorReq.onsuccess = onsuccess;
} }
@ -255,10 +255,10 @@ export class Backend implements CryptoStore {
wantedStates: number[], wantedStates: number[],
): Promise<OutgoingRoomKeyRequest[]> { ): Promise<OutgoingRoomKeyRequest[]> {
let stateIndex = 0; let stateIndex = 0;
const results = []; const results: OutgoingRoomKeyRequest[] = [];
function onsuccess(ev) { function onsuccess(this: IDBRequest<IDBCursorWithValue>) {
const cursor = ev.target.result; const cursor = this.result;
if (cursor) { if (cursor) {
const keyReq = cursor.value; const keyReq = cursor.value;
if (keyReq.recipients.includes({ userId, deviceId })) { if (keyReq.recipients.includes({ userId, deviceId })) {
@ -274,7 +274,7 @@ export class Backend implements CryptoStore {
} }
const wantedState = wantedStates[stateIndex]; const wantedState = wantedStates[stateIndex];
const cursorReq = ev.target.source.openCursor(wantedState); const cursorReq = (this.source as IDBIndex).openCursor(wantedState);
cursorReq.onsuccess = onsuccess; cursorReq.onsuccess = onsuccess;
} }
} }
@ -306,10 +306,10 @@ export class Backend implements CryptoStore {
expectedState: number, expectedState: number,
updates: Partial<OutgoingRoomKeyRequest>, updates: Partial<OutgoingRoomKeyRequest>,
): Promise<OutgoingRoomKeyRequest | null> { ): Promise<OutgoingRoomKeyRequest | null> {
let result = null; let result: OutgoingRoomKeyRequest = null;
function onsuccess(ev) { function onsuccess(this: IDBRequest<IDBCursorWithValue>) {
const cursor = ev.target.result; const cursor = this.result;
if (!cursor) { if (!cursor) {
return; return;
} }
@ -444,7 +444,7 @@ export class Backend implements CryptoStore {
const objectStore = txn.objectStore("sessions"); const objectStore = txn.objectStore("sessions");
const idx = objectStore.index("deviceKey"); const idx = objectStore.index("deviceKey");
const getReq = idx.openCursor(deviceKey); const getReq = idx.openCursor(deviceKey);
const results = {}; const results: Parameters<Parameters<Backend["getEndToEndSessions"]>[2]>[0] = {};
getReq.onsuccess = function() { getReq.onsuccess = function() {
const cursor = getReq.result; const cursor = getReq.result;
if (cursor) { if (cursor) {
@ -734,7 +734,7 @@ export class Backend implements CryptoStore {
} }
public getEndToEndRooms(txn: IDBTransaction, func: (rooms: Record<string, IRoomEncryption>) => void): void { public getEndToEndRooms(txn: IDBTransaction, func: (rooms: Record<string, IRoomEncryption>) => void): void {
const rooms = {}; const rooms: Parameters<Parameters<Backend["getEndToEndRooms"]>[1]>[0] = {};
const objectStore = txn.objectStore("rooms"); const objectStore = txn.objectStore("rooms");
const getReq = objectStore.openCursor(); const getReq = objectStore.openCursor();
getReq.onsuccess = function() { getReq.onsuccess = function() {
@ -756,7 +756,7 @@ export class Backend implements CryptoStore {
public getSessionsNeedingBackup(limit: number): Promise<ISession[]> { public getSessionsNeedingBackup(limit: number): Promise<ISession[]> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const sessions = []; const sessions: ISession[] = [];
const txn = this.db.transaction( const txn = this.db.transaction(
["sessions_needing_backup", "inbound_group_sessions"], ["sessions_needing_backup", "inbound_group_sessions"],
@ -877,8 +877,8 @@ export class Backend implements CryptoStore {
func: (txn: IDBTransaction) => T, func: (txn: IDBTransaction) => T,
log: PrefixedLogger = logger, log: PrefixedLogger = logger,
): Promise<T> { ): Promise<T> {
let startTime; let startTime: number;
let description; let description: string;
if (PROFILE_TRANSACTIONS) { if (PROFILE_TRANSACTIONS) {
const txnId = this.nextTxnId++; const txnId = this.nextTxnId++;
startTime = Date.now(); startTime = Date.now();

View File

@ -175,8 +175,10 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore {
} }
public async filterOutNotifiedErrorDevices(devices: IOlmDevice[]): Promise<IOlmDevice[]> { public async filterOutNotifiedErrorDevices(devices: IOlmDevice[]): Promise<IOlmDevice[]> {
const notifiedErrorDevices = getJsonItem<string[]>(this.store, KEY_NOTIFIED_ERROR_DEVICES) || {}; const notifiedErrorDevices = getJsonItem<MemoryCryptoStore["notifiedErrorDevices"]>(
const ret = []; this.store, KEY_NOTIFIED_ERROR_DEVICES,
) || {};
const ret: IOlmDevice[] = [];
for (const device of devices) { for (const device of devices) {
const { userId, deviceInfo } = device; const { userId, deviceInfo } = device;
@ -291,7 +293,7 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore {
} }
public getEndToEndRooms(txn: unknown, func: (rooms: Record<string, IRoomEncryption>) => void): void { public getEndToEndRooms(txn: unknown, func: (rooms: Record<string, IRoomEncryption>) => void): void {
const result = {}; const result: Record<string, IRoomEncryption> = {};
const prefix = keyEndToEndRoomsPrefix(''); const prefix = keyEndToEndRoomsPrefix('');
for (let i = 0; i < this.store.length; ++i) { for (let i = 0; i < this.store.length; ++i) {
@ -306,7 +308,7 @@ export class LocalStorageCryptoStore extends MemoryCryptoStore {
public getSessionsNeedingBackup(limit: number): Promise<ISession[]> { public getSessionsNeedingBackup(limit: number): Promise<ISession[]> {
const sessionsNeedingBackup = getJsonItem<string[]>(this.store, KEY_SESSIONS_NEEDING_BACKUP) || {}; const sessionsNeedingBackup = getJsonItem<string[]>(this.store, KEY_SESSIONS_NEEDING_BACKUP) || {};
const sessions = []; const sessions: ISession[] = [];
for (const session in sessionsNeedingBackup) { for (const session in sessionsNeedingBackup) {
if (Object.prototype.hasOwnProperty.call(sessionsNeedingBackup, session)) { if (Object.prototype.hasOwnProperty.call(sessionsNeedingBackup, session)) {

View File

@ -30,4 +30,5 @@ export interface IVerificationChannel {
sendCompleted(type: string, content: Record<string, any>): Promise<void>; sendCompleted(type: string, content: Record<string, any>): Promise<void>;
completedContentFromEvent(event: MatrixEvent): Record<string, any>; completedContentFromEvent(event: MatrixEvent): Record<string, any>;
canCreateRequest(type: string): boolean; canCreateRequest(type: string): boolean;
handleEvent(event: MatrixEvent, request: VerificationRequest, isLiveEvent: boolean): Promise<void>;
} }

View File

@ -26,6 +26,7 @@ import { IVerificationChannel } from "./Channel";
import { EventType } from "../../../@types/event"; import { EventType } from "../../../@types/event";
import { MatrixClient } from "../../../client"; import { MatrixClient } from "../../../client";
import { MatrixEvent } from "../../../models/event"; import { MatrixEvent } from "../../../models/event";
import { IRequestsMap } from "../..";
const MESSAGE_TYPE = EventType.RoomMessage; const MESSAGE_TYPE = EventType.RoomMessage;
const M_REFERENCE = "m.reference"; 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<string, Map<string, VerificationRequest>>(); private requestsByRoomId = new Map<string, Map<string, VerificationRequest>>();
public getRequest(event: MatrixEvent): VerificationRequest { public getRequest(event: MatrixEvent): VerificationRequest {
@ -328,7 +329,7 @@ export class InRoomRequests {
this.doSetRequest(event.getRoomId(), InRoomChannel.getTransactionId(event), request); 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); this.doSetRequest(channel.roomId, channel.transactionId, request);
} }

View File

@ -30,8 +30,9 @@ import { errorFromEvent, newUnexpectedMessageError } from "../Error";
import { MatrixEvent } from "../../../models/event"; import { MatrixEvent } from "../../../models/event";
import { IVerificationChannel } from "./Channel"; import { IVerificationChannel } from "./Channel";
import { MatrixClient } from "../../../client"; import { MatrixClient } from "../../../client";
import { IRequestsMap } from '../..';
type Request = VerificationRequest<ToDeviceChannel>; export type Request = VerificationRequest<ToDeviceChannel>;
/** /**
* A key verification channel that sends verification events over to_device messages. * 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<string, any>, devices: string[]): Promise<void> { private async sendToDevices(type: string, content: Record<string, any>, devices: string[]): Promise<void> {
if (devices.length) { if (devices.length) {
const msgMap = {}; const msgMap: Record<string, Record<string, any>> = {};
for (const deviceId of devices) { for (const deviceId of devices) {
msgMap[deviceId] = content; msgMap[deviceId] = content;
} }
@ -294,7 +295,7 @@ export class ToDeviceChannel implements IVerificationChannel {
} }
} }
export class ToDeviceRequests { export class ToDeviceRequests implements IRequestsMap {
private requestsByUserId = new Map<string, Map<string, Request>>(); private requestsByUserId = new Map<string, Map<string, Request>>();
public getRequest(event: MatrixEvent): Request { public getRequest(event: MatrixEvent): Request {

View File

@ -582,7 +582,9 @@ export class VerificationRequest<C extends IVerificationChannel = IVerificationC
// get common methods // get common methods
if (phase === PHASE_REQUESTED || phase === PHASE_READY) { if (phase === PHASE_REQUESTED || phase === PHASE_READY) {
if (!this.wasSentByOwnDevice(event)) { if (!this.wasSentByOwnDevice(event)) {
const content = event.getContent(); const content = event.getContent<{
methods: string[];
}>();
this.commonMethods = this.commonMethods =
content.methods.filter(m => this.verificationMethods.has(m)); content.methods.filter(m => this.verificationMethods.has(m));
} }

View File

@ -282,7 +282,7 @@ export class MSC3089TreeSpace {
const members = this.room.currentState.getStateEvents(EventType.RoomMember); const members = this.room.currentState.getStateEvents(EventType.RoomMember);
for (const member of members) { for (const member of members) {
const isNotUs = member.getStateKey() !== this.client.getUserId(); const isNotUs = member.getStateKey() !== this.client.getUserId();
if (isNotUs && kickMemberships.includes(member.getContent<string>()['membership'])) { if (isNotUs && kickMemberships.includes(member.getContent().membership)) {
const stateKey = member.getStateKey(); const stateKey = member.getStateKey();
if (!stateKey) { if (!stateKey) {
throw new Error("State key not found for branch"); throw new Error("State key not found for branch");