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
Rust: Query backup on fail to decrypt similar to libolm (#3711)
* Refactor key backup recovery to prepare for rust * rust backup restore support * map decryption errors correctly from rust * query backup on fail to decrypt
This commit is contained in:
@@ -25,11 +25,11 @@ import { Room } from "../models/room";
|
||||
import { RoomMember } from "../models/room-member";
|
||||
import { BackupDecryptor, CryptoBackend, OnSyncCompletedData } from "../common-crypto/CryptoBackend";
|
||||
import { logger } from "../logger";
|
||||
import { IHttpOpts, MatrixHttpApi, Method } from "../http-api";
|
||||
import { ClientPrefix, IHttpOpts, MatrixHttpApi, Method } from "../http-api";
|
||||
import { RoomEncryptor } from "./RoomEncryptor";
|
||||
import { OutgoingRequest, OutgoingRequestProcessor } from "./OutgoingRequestProcessor";
|
||||
import { KeyClaimManager } from "./KeyClaimManager";
|
||||
import { MapWithDefault } from "../utils";
|
||||
import { MapWithDefault, encodeUri } from "../utils";
|
||||
import {
|
||||
BackupTrustInfo,
|
||||
BootstrapCrossSigningOpts,
|
||||
@@ -47,6 +47,7 @@ import {
|
||||
ImportRoomKeysOpts,
|
||||
KeyBackupCheck,
|
||||
KeyBackupInfo,
|
||||
KeyBackupSession,
|
||||
UserVerificationStatus,
|
||||
VerificationRequest,
|
||||
} from "../crypto-api";
|
||||
@@ -78,6 +79,8 @@ interface ISignableObject {
|
||||
unsigned?: object;
|
||||
}
|
||||
|
||||
const KEY_BACKUP_CHECK_RATE_LIMIT = 5000; // ms
|
||||
|
||||
/**
|
||||
* An implementation of {@link CryptoBackend} using the Rust matrix-sdk-crypto.
|
||||
*
|
||||
@@ -102,6 +105,8 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
||||
private crossSigningIdentity: CrossSigningIdentity;
|
||||
private readonly backupManager: RustBackupManager;
|
||||
|
||||
private sessionLastCheckAttemptedTime: Record<string, number> = {}; // When did we last try to check the server for a given session id?
|
||||
|
||||
private readonly reemitter = new TypedReEmitter<RustCryptoEvents, RustCryptoEventMap>(this);
|
||||
|
||||
public constructor(
|
||||
@@ -130,7 +135,7 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
||||
super();
|
||||
this.outgoingRequestProcessor = new OutgoingRequestProcessor(olmMachine, http);
|
||||
this.keyClaimManager = new KeyClaimManager(olmMachine, this.outgoingRequestProcessor);
|
||||
this.eventDecryptor = new EventDecryptor(olmMachine);
|
||||
this.eventDecryptor = new EventDecryptor(olmMachine, this);
|
||||
|
||||
this.backupManager = new RustBackupManager(olmMachine, http, this.outgoingRequestProcessor);
|
||||
this.reemitter.reEmit(this.backupManager, [
|
||||
@@ -142,6 +147,46 @@ export class RustCrypto extends TypedEventEmitter<RustCryptoEvents, RustCryptoEv
|
||||
this.crossSigningIdentity = new CrossSigningIdentity(olmMachine, this.outgoingRequestProcessor, secretStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to retrieve a session from a key backup, if enough time
|
||||
* has elapsed since the last check for this session id.
|
||||
*/
|
||||
public async queryKeyBackupRateLimited(targetRoomId: string, targetSessionId: string): Promise<void> {
|
||||
const backupKeys: RustSdkCryptoJs.BackupKeys = await this.olmMachine.getBackupKeys();
|
||||
if (!backupKeys.decryptionKey) return;
|
||||
const version = backupKeys.backupVersion;
|
||||
|
||||
const now = new Date().getTime();
|
||||
if (
|
||||
!this.sessionLastCheckAttemptedTime[targetSessionId!] ||
|
||||
now - this.sessionLastCheckAttemptedTime[targetSessionId!] > KEY_BACKUP_CHECK_RATE_LIMIT
|
||||
) {
|
||||
this.sessionLastCheckAttemptedTime[targetSessionId!] = now;
|
||||
|
||||
const path = encodeUri("/room_keys/keys/$roomId/$sessionId", {
|
||||
$roomId: targetRoomId,
|
||||
$sessionId: targetSessionId,
|
||||
});
|
||||
|
||||
const res = await this.http.authedRequest<KeyBackupSession>(Method.Get, path, { version }, undefined, {
|
||||
prefix: ClientPrefix.V3,
|
||||
});
|
||||
|
||||
if (this.stopped) return;
|
||||
|
||||
const backupDecryptor = new RustBackupDecryptor(backupKeys.decryptionKey);
|
||||
if (res) {
|
||||
const sessionsToImport: Record<string, KeyBackupSession> = {};
|
||||
sessionsToImport[targetSessionId] = res;
|
||||
const keys = await backupDecryptor.decryptSessions(sessionsToImport);
|
||||
for (const k of keys) {
|
||||
k.room_id = targetRoomId!;
|
||||
}
|
||||
await this.importRoomKeys(keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the OlmMachine only if {@link RustCrypto#stop} has not been called.
|
||||
*
|
||||
@@ -1383,7 +1428,7 @@ class EventDecryptor {
|
||||
() => new MapWithDefault<string, Set<MatrixEvent>>(() => new Set()),
|
||||
);
|
||||
|
||||
public constructor(private readonly olmMachine: RustSdkCryptoJs.OlmMachine) {}
|
||||
public constructor(private readonly olmMachine: RustSdkCryptoJs.OlmMachine, private readonly crypto: RustCrypto) {}
|
||||
|
||||
public async attemptEventDecryption(event: MatrixEvent): Promise<IEventDecryptionResult> {
|
||||
logger.info("Attempting decryption of event", event);
|
||||
@@ -1431,6 +1476,7 @@ class EventDecryptor {
|
||||
session: content.sender_key + "|" + content.session_id,
|
||||
},
|
||||
);
|
||||
this.crypto.queryKeyBackupRateLimited(event.getRoomId()!, event.getWireContent().session_id!);
|
||||
break;
|
||||
}
|
||||
case RustSdkCryptoJs.DecryptionErrorCode.UnknownMessageIndex: {
|
||||
@@ -1441,6 +1487,7 @@ class EventDecryptor {
|
||||
session: content.sender_key + "|" + content.session_id,
|
||||
},
|
||||
);
|
||||
this.crypto.queryKeyBackupRateLimited(event.getRoomId()!, event.getWireContent().session_id!);
|
||||
break;
|
||||
}
|
||||
// We don't map MismatchedIdentityKeys for now, as there is no equivalent in legacy.
|
||||
|
||||
Reference in New Issue
Block a user