You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-11-23 17:02:25 +03:00
Implement CryptoApi.checkKeyBackupAndEnable (#3633)
* Implement `CryptoApi.checkKeyBackup` * Deprecate `MatrixClient.enableKeyBackup`. * fix integ test * more tests --------- Co-authored-by: valere <valeref@matrix.org>
This commit is contained in:
committed by
GitHub
parent
16ddcb0ed0
commit
3f7af189e4
@@ -17,20 +17,33 @@ limitations under the License.
|
||||
import { OlmMachine, SignatureVerification } from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
import * as RustSdkCryptoJs from "@matrix-org/matrix-sdk-crypto-wasm";
|
||||
|
||||
import { BackupTrustInfo, Curve25519AuthData, KeyBackupInfo } from "../crypto-api/keybackup";
|
||||
import { BackupTrustInfo, Curve25519AuthData, KeyBackupCheck, KeyBackupInfo } from "../crypto-api/keybackup";
|
||||
import { logger } from "../logger";
|
||||
import { ClientPrefix, IHttpOpts, MatrixError, MatrixHttpApi, Method } from "../http-api";
|
||||
import { CryptoEvent } from "../crypto";
|
||||
import { TypedEventEmitter } from "../models/typed-event-emitter";
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
export class RustBackupManager {
|
||||
public constructor(private readonly olmMachine: OlmMachine) {}
|
||||
export class RustBackupManager extends TypedEventEmitter<RustBackupCryptoEvents, RustBackupCryptoEventMap> {
|
||||
/** Have we checked if there is a backup on the server which we can use */
|
||||
private checkedForBackup = false;
|
||||
private activeBackupVersion: string | null = null;
|
||||
|
||||
public constructor(
|
||||
private readonly olmMachine: OlmMachine,
|
||||
private readonly http: MatrixHttpApi<IHttpOpts & { onlyData: true }>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the backup version we are currently backing up to, if any
|
||||
*/
|
||||
public async getActiveBackupVersion(): Promise<string | null> {
|
||||
// TODO stub
|
||||
return null;
|
||||
if (!this.olmMachine.isBackupEnabled()) return null;
|
||||
return this.activeBackupVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -52,4 +65,138 @@ export class RustBackupManager {
|
||||
trusted: signatureVerification.trusted(),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-check the key backup and enable/disable it as appropriate.
|
||||
*
|
||||
* @param force - whether we should force a re-check even if one has already happened.
|
||||
*/
|
||||
public checkKeyBackupAndEnable(force: boolean): Promise<KeyBackupCheck | null> {
|
||||
if (!force && this.checkedForBackup) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
// make sure there is only one check going on at a time
|
||||
if (!this.keyBackupCheckInProgress) {
|
||||
this.keyBackupCheckInProgress = this.doCheckKeyBackup().finally(() => {
|
||||
this.keyBackupCheckInProgress = null;
|
||||
});
|
||||
}
|
||||
return this.keyBackupCheckInProgress;
|
||||
}
|
||||
private keyBackupCheckInProgress: Promise<KeyBackupCheck | null> | null = null;
|
||||
|
||||
/** Helper for `checkKeyBackup` */
|
||||
private async doCheckKeyBackup(): Promise<KeyBackupCheck | null> {
|
||||
logger.log("Checking key backup status...");
|
||||
let backupInfo: KeyBackupInfo | null = null;
|
||||
try {
|
||||
backupInfo = await this.requestKeyBackupVersion();
|
||||
} catch (e) {
|
||||
logger.warn("Error checking for active key backup", e);
|
||||
return null;
|
||||
}
|
||||
this.checkedForBackup = true;
|
||||
|
||||
if (backupInfo && !backupInfo.version) {
|
||||
logger.warn("active backup lacks a useful 'version'; ignoring it");
|
||||
}
|
||||
|
||||
const activeVersion = await this.getActiveBackupVersion();
|
||||
|
||||
if (!backupInfo) {
|
||||
if (activeVersion !== null) {
|
||||
logger.log("No key backup present on server: disabling key backup");
|
||||
await this.disableKeyBackup();
|
||||
} else {
|
||||
logger.log("No key backup present on server: not enabling key backup");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
const trustInfo = await this.isKeyBackupTrusted(backupInfo);
|
||||
|
||||
if (!trustInfo.trusted) {
|
||||
if (activeVersion !== null) {
|
||||
logger.log("Key backup present on server but not trusted: disabling key backup");
|
||||
await this.disableKeyBackup();
|
||||
} else {
|
||||
logger.log("Key backup present on server but not trusted: not enabling key backup");
|
||||
}
|
||||
} else {
|
||||
if (activeVersion === null) {
|
||||
logger.log(`Found usable key backup v${backupInfo.version}: enabling key backups`);
|
||||
await this.enableKeyBackup(backupInfo);
|
||||
} else if (activeVersion !== backupInfo.version) {
|
||||
logger.log(`On backup version ${activeVersion} but found version ${backupInfo.version}: switching.`);
|
||||
await this.disableKeyBackup();
|
||||
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
|
||||
// because we don't remember what backup version we uploaded keys to:
|
||||
// see https://github.com/vector-im/element-web/issues/14833
|
||||
await this.scheduleAllGroupSessionsForBackup();
|
||||
} else {
|
||||
logger.log(`Backup version ${backupInfo.version} still current`);
|
||||
}
|
||||
}
|
||||
return { backupInfo, trustInfo };
|
||||
}
|
||||
|
||||
private async enableKeyBackup(backupInfo: KeyBackupInfo): Promise<void> {
|
||||
// we know for certain it must be a Curve25519 key, because we have verified it and only Curve25519
|
||||
// keys can be verified.
|
||||
//
|
||||
// we also checked it has a valid `version`.
|
||||
await this.olmMachine.enableBackupV1(
|
||||
(backupInfo.auth_data as Curve25519AuthData).public_key,
|
||||
backupInfo.version!,
|
||||
);
|
||||
this.activeBackupVersion = backupInfo.version!;
|
||||
|
||||
this.emit(CryptoEvent.KeyBackupStatus, true);
|
||||
|
||||
// TODO: kick off an upload loop
|
||||
}
|
||||
|
||||
private async disableKeyBackup(): Promise<void> {
|
||||
await this.olmMachine.disableBackup();
|
||||
this.activeBackupVersion = null;
|
||||
this.emit(CryptoEvent.KeyBackupStatus, false);
|
||||
}
|
||||
|
||||
private async scheduleAllGroupSessionsForBackup(): Promise<void> {
|
||||
// TODO stub
|
||||
}
|
||||
/**
|
||||
* Get information about the current key backup from the server
|
||||
*
|
||||
* @returns Information object from API or null if there is no active backup.
|
||||
*/
|
||||
private async requestKeyBackupVersion(): Promise<KeyBackupInfo | null> {
|
||||
try {
|
||||
return await this.http.authedRequest<KeyBackupInfo>(
|
||||
Method.Get,
|
||||
"/room_keys/version",
|
||||
undefined,
|
||||
undefined,
|
||||
{
|
||||
prefix: ClientPrefix.V3,
|
||||
},
|
||||
);
|
||||
} catch (e) {
|
||||
if ((<MatrixError>e).errcode === "M_NOT_FOUND") {
|
||||
return null;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export type RustBackupCryptoEvents = CryptoEvent.KeyBackupStatus;
|
||||
|
||||
export type RustBackupCryptoEventMap = {
|
||||
[CryptoEvent.KeyBackupStatus]: (enabled: boolean) => void;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user