Matrix: reload backup keys after reset

This commit is contained in:
Gustavo Madeira Santana
2026-03-12 20:48:44 +00:00
parent 9d17de6bdb
commit e74a3cfc15
2 changed files with 98 additions and 1 deletions

View File

@@ -1203,6 +1203,51 @@ describe("MatrixClient crypto bootstrapping", () => {
expect(loadSessionBackupPrivateKeyFromSecretStorage).toHaveBeenCalledTimes(1);
});
it("reloads backup keys from secret storage when the cached key mismatches the active backup", async () => {
const loadSessionBackupPrivateKeyFromSecretStorage = vi.fn(async () => {});
const checkKeyBackupAndEnable = vi.fn(async () => {});
const isKeyBackupTrusted = vi
.fn()
.mockResolvedValueOnce({
trusted: true,
matchesDecryptionKey: false,
})
.mockResolvedValueOnce({
trusted: true,
matchesDecryptionKey: true,
});
matrixJsClient.getCrypto = vi.fn(() => ({
on: vi.fn(),
getActiveSessionBackupVersion: vi.fn(async () => "49262"),
getSessionBackupPrivateKey: vi.fn(async () => new Uint8Array([1])),
loadSessionBackupPrivateKeyFromSecretStorage,
checkKeyBackupAndEnable,
getKeyBackupInfo: vi.fn(async () => ({
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
auth_data: {},
version: "49262",
})),
isKeyBackupTrusted,
}));
const client = new MatrixClient("https://matrix.example.org", "token", undefined, undefined, {
encryption: true,
});
const backup = await client.getRoomKeyBackupStatus();
expect(backup).toMatchObject({
serverVersion: "49262",
activeVersion: "49262",
trusted: true,
matchesDecryptionKey: true,
decryptionKeyCached: true,
keyLoadAttempted: true,
keyLoadError: null,
});
expect(loadSessionBackupPrivateKeyFromSecretStorage).toHaveBeenCalledTimes(1);
expect(checkKeyBackupAndEnable).toHaveBeenCalledTimes(1);
});
it("reports why backup key loading failed during status checks", async () => {
const loadSessionBackupPrivateKeyFromSecretStorage = vi.fn(async () => {
throw new Error("secret storage key is not available");
@@ -1387,6 +1432,57 @@ describe("MatrixClient crypto bootstrapping", () => {
expect(checkKeyBackupAndEnable).toHaveBeenCalledTimes(1);
});
it("reloads the new backup decryption key after reset when the old cached key mismatches", async () => {
const checkKeyBackupAndEnable = vi.fn(async () => {});
const bootstrapSecretStorage = vi.fn(async () => {});
const loadSessionBackupPrivateKeyFromSecretStorage = vi.fn(async () => {});
const isKeyBackupTrusted = vi
.fn()
.mockResolvedValueOnce({
trusted: true,
matchesDecryptionKey: false,
})
.mockResolvedValueOnce({
trusted: true,
matchesDecryptionKey: true,
});
matrixJsClient.getCrypto = vi.fn(() => ({
on: vi.fn(),
bootstrapSecretStorage,
checkKeyBackupAndEnable,
loadSessionBackupPrivateKeyFromSecretStorage,
getActiveSessionBackupVersion: vi.fn(async () => "49262"),
getSessionBackupPrivateKey: vi.fn(async () => new Uint8Array([1])),
getKeyBackupInfo: vi.fn(async () => ({
algorithm: "m.megolm_backup.v1.curve25519-aes-sha2",
auth_data: {},
version: "49262",
})),
isKeyBackupTrusted,
}));
const client = new MatrixClient("https://matrix.example.org", "token", undefined, undefined, {
encryption: true,
});
vi.spyOn(client, "doRequest").mockImplementation(async (method, endpoint) => {
if (method === "GET" && String(endpoint).includes("/room_keys/version")) {
return { version: "22245" };
}
if (method === "DELETE" && String(endpoint).includes("/room_keys/version/22245")) {
return {};
}
return {};
});
const result = await client.resetRoomKeyBackup();
expect(result.success).toBe(true);
expect(result.createdVersion).toBe("49262");
expect(result.backup.matchesDecryptionKey).toBe(true);
expect(loadSessionBackupPrivateKeyFromSecretStorage).toHaveBeenCalledTimes(1);
expect(checkKeyBackupAndEnable).toHaveBeenCalledTimes(2);
});
it("fails reset when the recreated backup still does not match the local decryption key", async () => {
matrixJsClient.getCrypto = vi.fn(() => ({
on: vi.fn(),

View File

@@ -708,7 +708,7 @@ export class MatrixClient {
await this.resolveRoomKeyBackupTrustState(crypto, serverVersionFallback);
let keyLoadAttempted = false;
let keyLoadError: string | null = null;
if (serverVersion && decryptionKeyCached === false) {
if (serverVersion && (decryptionKeyCached === false || matchesDecryptionKey === false)) {
if (
typeof crypto.loadSessionBackupPrivateKeyFromSecretStorage ===
"function" /* pragma: allowlist secret */
@@ -716,6 +716,7 @@ export class MatrixClient {
keyLoadAttempted = true;
try {
await crypto.loadSessionBackupPrivateKeyFromSecretStorage(); // pragma: allowlist secret
await this.enableTrustedRoomKeyBackupIfPossible(crypto);
} catch (err) {
keyLoadError = err instanceof Error ? err.message : String(err);
}