You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-28 05:03:59 +03:00
Apply more strict typescript around the codebase (#2778)
* Apply more strict typescript around the codebase * Fix tests * Revert strict mode commit * Iterate strict * Iterate * Iterate strict * Iterate * Fix tests * Iterate * Iterate strict * Add tests * Iterate * Iterate * Fix tests * Fix tests * Strict types be strict * Fix types * detectOpenHandles * Strict * Fix client not stopping * Add sync peeking tests * Make test happier * More strict * Iterate * Stabilise * Moar strictness * Improve coverage * Fix types * Fix types * Improve types further * Fix types * Improve typing of NamespacedValue * Fix types
This commit is contained in:
committed by
GitHub
parent
fdbbd9bca4
commit
867a0ca7ee
@@ -40,6 +40,7 @@ import {
|
||||
import { UnstableValue } from "../NamespacedValue";
|
||||
import { CryptoEvent, IMegolmSessionData } from "./index";
|
||||
import { crypto } from "./crypto";
|
||||
import { HTTPError, MatrixError } from "../http-api";
|
||||
|
||||
const KEY_BACKUP_KEYS_PER_REQUEST = 200;
|
||||
const KEY_BACKUP_CHECK_RATE_LIMIT = 5000; // ms
|
||||
@@ -62,7 +63,7 @@ export type TrustInfo = {
|
||||
};
|
||||
|
||||
export interface IKeyBackupCheck {
|
||||
backupInfo: IKeyBackupInfo;
|
||||
backupInfo?: IKeyBackupInfo;
|
||||
trustInfo: TrustInfo;
|
||||
}
|
||||
|
||||
@@ -85,9 +86,7 @@ interface BackupAlgorithmClass {
|
||||
init(authData: AuthData, getKey: GetKey): Promise<BackupAlgorithm>;
|
||||
|
||||
// prepare a brand new backup
|
||||
prepare(
|
||||
key: string | Uint8Array | null,
|
||||
): Promise<[Uint8Array, AuthData]>;
|
||||
prepare(key?: string | Uint8Array | null): Promise<[Uint8Array, AuthData]>;
|
||||
|
||||
checkBackupVersion(info: IKeyBackupInfo): void;
|
||||
}
|
||||
@@ -221,19 +220,19 @@ export class BackupManager {
|
||||
* one of the user's verified devices, start backing up
|
||||
* to it.
|
||||
*/
|
||||
public async checkAndStart(): Promise<IKeyBackupCheck> {
|
||||
public async checkAndStart(): Promise<IKeyBackupCheck | null> {
|
||||
logger.log("Checking key backup status...");
|
||||
if (this.baseApis.isGuest()) {
|
||||
logger.log("Skipping key backup check since user is guest");
|
||||
this.checkedForBackup = true;
|
||||
return null;
|
||||
}
|
||||
let backupInfo: IKeyBackupInfo;
|
||||
let backupInfo: IKeyBackupInfo | undefined;
|
||||
try {
|
||||
backupInfo = await this.baseApis.getKeyBackupVersion();
|
||||
backupInfo = await this.baseApis.getKeyBackupVersion() ?? undefined;
|
||||
} catch (e) {
|
||||
logger.log("Error checking for active key backup", e);
|
||||
if (e.httpStatus === 404) {
|
||||
if ((<HTTPError>e).httpStatus === 404) {
|
||||
// 404 is returned when the key backup does not exist, so that
|
||||
// counts as successfully checking.
|
||||
this.checkedForBackup = true;
|
||||
@@ -245,11 +244,8 @@ export class BackupManager {
|
||||
const trustInfo = await this.isKeyBackupTrusted(backupInfo);
|
||||
|
||||
if (trustInfo.usable && !this.backupInfo) {
|
||||
logger.log(
|
||||
"Found usable key backup v" + backupInfo.version +
|
||||
": enabling key backups",
|
||||
);
|
||||
await this.enableKeyBackup(backupInfo);
|
||||
logger.log(`Found usable key backup v${backupInfo!.version}: enabling key backups`);
|
||||
await this.enableKeyBackup(backupInfo!);
|
||||
} else if (!trustInfo.usable && this.backupInfo) {
|
||||
logger.log("No usable key backup: disabling key backup");
|
||||
this.disableKeyBackup();
|
||||
@@ -257,13 +253,11 @@ export class BackupManager {
|
||||
logger.log("No usable key backup: not enabling key backup");
|
||||
} else if (trustInfo.usable && this.backupInfo) {
|
||||
// may not be the same version: if not, we should switch
|
||||
if (backupInfo.version !== this.backupInfo.version) {
|
||||
logger.log(
|
||||
"On backup version " + this.backupInfo.version + " but found " +
|
||||
"version " + backupInfo.version + ": switching.",
|
||||
);
|
||||
if (backupInfo!.version !== this.backupInfo.version) {
|
||||
logger.log(`On backup version ${this.backupInfo.version} but ` +
|
||||
`found version ${backupInfo!.version}: switching.`);
|
||||
this.disableKeyBackup();
|
||||
await this.enableKeyBackup(backupInfo);
|
||||
await this.enableKeyBackup(backupInfo!);
|
||||
// We're now using a new backup, so schedule all the keys we have to be
|
||||
// uploaded to the new backup. This is a bit of a workaround to upload
|
||||
// keys to a new backup in *most* cases, but it won't cover all cases
|
||||
@@ -271,7 +265,7 @@ export class BackupManager {
|
||||
// see https://github.com/vector-im/element-web/issues/14833
|
||||
await this.scheduleAllGroupSessionsForBackup();
|
||||
} else {
|
||||
logger.log("Backup version " + backupInfo.version + " still current");
|
||||
logger.log(`Backup version ${backupInfo!.version} still current`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,7 +281,7 @@ export class BackupManager {
|
||||
* trust information (as returned by isKeyBackupTrusted)
|
||||
* in trustInfo.
|
||||
*/
|
||||
public async checkKeyBackup(): Promise<IKeyBackupCheck> {
|
||||
public async checkKeyBackup(): Promise<IKeyBackupCheck | null> {
|
||||
this.checkedForBackup = false;
|
||||
return this.checkAndStart();
|
||||
}
|
||||
@@ -325,7 +319,7 @@ export class BackupManager {
|
||||
* ]
|
||||
* }
|
||||
*/
|
||||
public async isKeyBackupTrusted(backupInfo: IKeyBackupInfo): Promise<TrustInfo> {
|
||||
public async isKeyBackupTrusted(backupInfo?: IKeyBackupInfo): Promise<TrustInfo> {
|
||||
const ret = {
|
||||
usable: false,
|
||||
trusted_locally: false,
|
||||
@@ -342,9 +336,10 @@ export class BackupManager {
|
||||
return ret;
|
||||
}
|
||||
|
||||
const privKey = await this.baseApis.crypto.getSessionBackupPrivateKey();
|
||||
const userId = this.baseApis.getUserId()!;
|
||||
const privKey = await this.baseApis.crypto!.getSessionBackupPrivateKey();
|
||||
if (privKey) {
|
||||
let algorithm;
|
||||
let algorithm: BackupAlgorithm | null = null;
|
||||
try {
|
||||
algorithm = await BackupManager.makeAlgorithm(backupInfo, async () => privKey);
|
||||
|
||||
@@ -356,13 +351,11 @@ export class BackupManager {
|
||||
// do nothing -- if we have an error, then we don't mark it as
|
||||
// locally trusted
|
||||
} finally {
|
||||
if (algorithm) {
|
||||
algorithm.free();
|
||||
}
|
||||
algorithm?.free();
|
||||
}
|
||||
}
|
||||
|
||||
const mySigs = backupInfo.auth_data.signatures[this.baseApis.getUserId()] || {};
|
||||
const mySigs = backupInfo.auth_data.signatures[userId] || {};
|
||||
|
||||
for (const keyId of Object.keys(mySigs)) {
|
||||
const keyIdParts = keyId.split(':');
|
||||
@@ -375,14 +368,14 @@ export class BackupManager {
|
||||
const sigInfo: SigInfo = { deviceId: keyIdParts[1] };
|
||||
|
||||
// first check to see if it's from our cross-signing key
|
||||
const crossSigningId = this.baseApis.crypto.crossSigningInfo.getId();
|
||||
const crossSigningId = this.baseApis.crypto!.crossSigningInfo.getId();
|
||||
if (crossSigningId === sigInfo.deviceId) {
|
||||
sigInfo.crossSigningId = true;
|
||||
try {
|
||||
await verifySignature(
|
||||
this.baseApis.crypto.olmDevice,
|
||||
this.baseApis.crypto!.olmDevice,
|
||||
backupInfo.auth_data,
|
||||
this.baseApis.getUserId(),
|
||||
userId,
|
||||
sigInfo.deviceId,
|
||||
crossSigningId,
|
||||
);
|
||||
@@ -400,17 +393,16 @@ export class BackupManager {
|
||||
// Now look for a sig from a device
|
||||
// At some point this can probably go away and we'll just support
|
||||
// it being signed by the cross-signing master key
|
||||
const device = this.baseApis.crypto.deviceList.getStoredDevice(
|
||||
this.baseApis.getUserId(), sigInfo.deviceId,
|
||||
const device = this.baseApis.crypto!.deviceList.getStoredDevice(userId, sigInfo.deviceId,
|
||||
);
|
||||
if (device) {
|
||||
sigInfo.device = device;
|
||||
sigInfo.deviceTrust = this.baseApis.checkDeviceTrust(this.baseApis.getUserId(), sigInfo.deviceId);
|
||||
sigInfo.deviceTrust = this.baseApis.checkDeviceTrust(userId, sigInfo.deviceId);
|
||||
try {
|
||||
await verifySignature(
|
||||
this.baseApis.crypto.olmDevice,
|
||||
this.baseApis.crypto!.olmDevice,
|
||||
backupInfo.auth_data,
|
||||
this.baseApis.getUserId(),
|
||||
userId,
|
||||
device.deviceId,
|
||||
device.getFingerprint(),
|
||||
);
|
||||
@@ -431,12 +423,7 @@ export class BackupManager {
|
||||
}
|
||||
|
||||
ret.usable = ret.sigs.some((s) => {
|
||||
return (
|
||||
s.valid && (
|
||||
(s.device && s.deviceTrust.isVerified()) ||
|
||||
(s.crossSigningId)
|
||||
)
|
||||
);
|
||||
return s.valid && ((s.device && s.deviceTrust?.isVerified()) || (s.crossSigningId));
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
@@ -474,17 +461,17 @@ export class BackupManager {
|
||||
} catch (err) {
|
||||
numFailures++;
|
||||
logger.log("Key backup request failed", err);
|
||||
if (err.data) {
|
||||
if ((<MatrixError>err).data) {
|
||||
if (
|
||||
err.data.errcode == 'M_NOT_FOUND' ||
|
||||
err.data.errcode == 'M_WRONG_ROOM_KEYS_VERSION'
|
||||
(<MatrixError>err).data.errcode == 'M_NOT_FOUND' ||
|
||||
(<MatrixError>err).data.errcode == 'M_WRONG_ROOM_KEYS_VERSION'
|
||||
) {
|
||||
// Re-check key backup status on error, so we can be
|
||||
// sure to present the current situation when asked.
|
||||
await this.checkKeyBackup();
|
||||
// Backup version has changed or this backup version
|
||||
// has been deleted
|
||||
this.baseApis.crypto.emit(CryptoEvent.KeyBackupFailed, err.data.errcode);
|
||||
this.baseApis.crypto!.emit(CryptoEvent.KeyBackupFailed, (<MatrixError>err).data.errcode!);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -507,50 +494,50 @@ export class BackupManager {
|
||||
* @returns {number} Number of sessions backed up
|
||||
*/
|
||||
public async backupPendingKeys(limit: number): Promise<number> {
|
||||
const sessions = await this.baseApis.crypto.cryptoStore.getSessionsNeedingBackup(limit);
|
||||
const sessions = await this.baseApis.crypto!.cryptoStore.getSessionsNeedingBackup(limit);
|
||||
if (!sessions.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
let remaining = await this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup();
|
||||
this.baseApis.crypto.emit(CryptoEvent.KeyBackupSessionsRemaining, remaining);
|
||||
let remaining = await this.baseApis.crypto!.cryptoStore.countSessionsNeedingBackup();
|
||||
this.baseApis.crypto!.emit(CryptoEvent.KeyBackupSessionsRemaining, remaining);
|
||||
|
||||
const rooms: IKeyBackup["rooms"] = {};
|
||||
for (const session of sessions) {
|
||||
const roomId = session.sessionData.room_id;
|
||||
const roomId = session.sessionData!.room_id;
|
||||
if (rooms[roomId] === undefined) {
|
||||
rooms[roomId] = { sessions: {} };
|
||||
}
|
||||
|
||||
const sessionData = this.baseApis.crypto.olmDevice.exportInboundGroupSession(
|
||||
session.senderKey, session.sessionId, session.sessionData,
|
||||
const sessionData = this.baseApis.crypto!.olmDevice.exportInboundGroupSession(
|
||||
session.senderKey, session.sessionId, session.sessionData!,
|
||||
);
|
||||
sessionData.algorithm = MEGOLM_ALGORITHM;
|
||||
|
||||
const forwardedCount =
|
||||
(sessionData.forwarding_curve25519_key_chain || []).length;
|
||||
|
||||
const userId = this.baseApis.crypto.deviceList.getUserByIdentityKey(
|
||||
const userId = this.baseApis.crypto!.deviceList.getUserByIdentityKey(
|
||||
MEGOLM_ALGORITHM, session.senderKey,
|
||||
);
|
||||
const device = this.baseApis.crypto.deviceList.getDeviceByIdentityKey(
|
||||
const device = this.baseApis.crypto!.deviceList.getDeviceByIdentityKey(
|
||||
MEGOLM_ALGORITHM, session.senderKey,
|
||||
);
|
||||
const verified = this.baseApis.crypto.checkDeviceInfoTrust(userId, device).isVerified();
|
||||
) ?? undefined;
|
||||
const verified = this.baseApis.crypto!.checkDeviceInfoTrust(userId!, device).isVerified();
|
||||
|
||||
rooms[roomId]['sessions'][session.sessionId] = {
|
||||
first_message_index: sessionData.first_known_index,
|
||||
forwarded_count: forwardedCount,
|
||||
is_verified: verified,
|
||||
session_data: await this.algorithm.encryptSession(sessionData),
|
||||
session_data: await this.algorithm!.encryptSession(sessionData),
|
||||
};
|
||||
}
|
||||
|
||||
await this.baseApis.sendKeyBackup(undefined, undefined, this.backupInfo.version, { rooms });
|
||||
await this.baseApis.sendKeyBackup(undefined, undefined, this.backupInfo!.version, { rooms });
|
||||
|
||||
await this.baseApis.crypto.cryptoStore.unmarkSessionsNeedingBackup(sessions);
|
||||
remaining = await this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup();
|
||||
this.baseApis.crypto.emit(CryptoEvent.KeyBackupSessionsRemaining, remaining);
|
||||
await this.baseApis.crypto!.cryptoStore.unmarkSessionsNeedingBackup(sessions);
|
||||
remaining = await this.baseApis.crypto!.cryptoStore.countSessionsNeedingBackup();
|
||||
this.baseApis.crypto!.emit(CryptoEvent.KeyBackupSessionsRemaining, remaining);
|
||||
|
||||
return sessions.length;
|
||||
}
|
||||
@@ -558,7 +545,7 @@ export class BackupManager {
|
||||
public async backupGroupSession(
|
||||
senderKey: string, sessionId: string,
|
||||
): Promise<void> {
|
||||
await this.baseApis.crypto.cryptoStore.markSessionsNeedingBackup([{
|
||||
await this.baseApis.crypto!.cryptoStore.markSessionsNeedingBackup([{
|
||||
senderKey: senderKey,
|
||||
sessionId: sessionId,
|
||||
}]);
|
||||
@@ -590,22 +577,22 @@ export class BackupManager {
|
||||
* (which will be equal to the number of sessions in the store).
|
||||
*/
|
||||
public async flagAllGroupSessionsForBackup(): Promise<number> {
|
||||
await this.baseApis.crypto.cryptoStore.doTxn(
|
||||
await this.baseApis.crypto!.cryptoStore.doTxn(
|
||||
'readwrite',
|
||||
[
|
||||
IndexedDBCryptoStore.STORE_INBOUND_GROUP_SESSIONS,
|
||||
IndexedDBCryptoStore.STORE_BACKUP,
|
||||
],
|
||||
(txn) => {
|
||||
this.baseApis.crypto.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (session) => {
|
||||
this.baseApis.crypto!.cryptoStore.getAllEndToEndInboundGroupSessions(txn, (session) => {
|
||||
if (session !== null) {
|
||||
this.baseApis.crypto.cryptoStore.markSessionsNeedingBackup([session], txn);
|
||||
this.baseApis.crypto!.cryptoStore.markSessionsNeedingBackup([session], txn);
|
||||
}
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
const remaining = await this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup();
|
||||
const remaining = await this.baseApis.crypto!.cryptoStore.countSessionsNeedingBackup();
|
||||
this.baseApis.emit(CryptoEvent.KeyBackupSessionsRemaining, remaining);
|
||||
return remaining;
|
||||
}
|
||||
@@ -615,7 +602,7 @@ export class BackupManager {
|
||||
* @returns {Promise<int>} Resolves to the number of sessions requiring backup
|
||||
*/
|
||||
public countSessionsNeedingBackup(): Promise<number> {
|
||||
return this.baseApis.crypto.cryptoStore.countSessionsNeedingBackup();
|
||||
return this.baseApis.crypto!.cryptoStore.countSessionsNeedingBackup();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -641,7 +628,7 @@ export class Curve25519 implements BackupAlgorithm {
|
||||
}
|
||||
|
||||
public static async prepare(
|
||||
key: string | Uint8Array | null,
|
||||
key?: string | Uint8Array | null,
|
||||
): Promise<[Uint8Array, AuthData]> {
|
||||
const decryption = new global.Olm.PkDecryption();
|
||||
try {
|
||||
@@ -741,7 +728,10 @@ function randomBytes(size: number): Uint8Array {
|
||||
return buf;
|
||||
}
|
||||
|
||||
const UNSTABLE_MSC3270_NAME = new UnstableValue(null, "org.matrix.msc3270.v1.aes-hmac-sha2");
|
||||
const UNSTABLE_MSC3270_NAME = new UnstableValue(
|
||||
"m.megolm_backup.v1.aes-hmac-sha2",
|
||||
"org.matrix.msc3270.v1.aes-hmac-sha2",
|
||||
);
|
||||
|
||||
export class Aes256 implements BackupAlgorithm {
|
||||
public static algorithmName = UNSTABLE_MSC3270_NAME.name;
|
||||
@@ -769,7 +759,7 @@ export class Aes256 implements BackupAlgorithm {
|
||||
}
|
||||
|
||||
public static async prepare(
|
||||
key: string | Uint8Array | null,
|
||||
key?: string | Uint8Array | null,
|
||||
): Promise<[Uint8Array, AuthData]> {
|
||||
let outKey: Uint8Array;
|
||||
const authData: Partial<IAes256AuthData> = {};
|
||||
|
||||
Reference in New Issue
Block a user