From 5d006f0caed3218111ccb2f71bc8494396586548 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 5 Sep 2025 09:28:02 +0100 Subject: [PATCH 1/6] Fix enabling key backup not working if there is an untrusted key backup on the server. --- .../encryption/KeyStoragePanelViewModel.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts index ee301bd27fc..2f721fa02a9 100644 --- a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts +++ b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts @@ -79,12 +79,26 @@ export function useKeyStoragePanelViewModel(): KeyStoragePanelState { return; } if (enable) { + logger.info("User requested enabling key backup"); + let currentKeyBackup = await crypto.checkKeyBackupAndEnable(); + if (currentKeyBackup) { + logger.info(`Existing key backup is present. version: ${currentKeyBackup.backupInfo.version}`, currentKeyBackup.trustInfo); + // Check if the current key backup can be used. Either of these properties causes the key backup to be used. + if (currentKeyBackup?.trustInfo.trusted || currentKeyBackup.trustInfo.matchesDecryptionKey) { + logger.info("Existing key backup can be used"); + } else { + logger.warn("Existing key backup cannot be used, creating new backup"); + // There aren't any *usable* backups, so we need to create a new one. + currentKeyBackup = null; + } + } else { + logger.info("No existing key backup versions are present, creating new backup"); + } + // If there is no existing key backup on the server, create one. // `resetKeyBackup` will delete any existing backup, so we only do this if there is no existing backup. - const currentKeyBackup = await crypto.checkKeyBackupAndEnable(); if (currentKeyBackup === null) { await crypto.resetKeyBackup(); - // resetKeyBackup fires this off in the background without waiting, so we need to do it // explicitly and wait for it, otherwise it won't be enabled yet when we check again. await crypto.checkKeyBackupAndEnable(); @@ -93,6 +107,7 @@ export function useKeyStoragePanelViewModel(): KeyStoragePanelState { // Set the flag so that EX no longer thinks the user wants backup disabled await matrixClient.setAccountData(BACKUP_DISABLED_ACCOUNT_DATA_KEY, { disabled: false }); } else { + logger.info("User requested disabling key backup"); // This method will delete the key backup as well as server side recovery keys and other // server-side crypto data. await crypto.disableKeyStorage(); From fa056e8a48cf242e23a6ae6648d285343e2600f2 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 5 Sep 2025 09:36:40 +0100 Subject: [PATCH 2/6] lint --- .../settings/encryption/KeyStoragePanelViewModel.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts index 2f721fa02a9..1c24f2dd365 100644 --- a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts +++ b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts @@ -82,7 +82,10 @@ export function useKeyStoragePanelViewModel(): KeyStoragePanelState { logger.info("User requested enabling key backup"); let currentKeyBackup = await crypto.checkKeyBackupAndEnable(); if (currentKeyBackup) { - logger.info(`Existing key backup is present. version: ${currentKeyBackup.backupInfo.version}`, currentKeyBackup.trustInfo); + logger.info( + `Existing key backup is present. version: ${currentKeyBackup.backupInfo.version}`, + currentKeyBackup.trustInfo, + ); // Check if the current key backup can be used. Either of these properties causes the key backup to be used. if (currentKeyBackup?.trustInfo.trusted || currentKeyBackup.trustInfo.matchesDecryptionKey) { logger.info("Existing key backup can be used"); From 065bf203de4b19cb11d0e867cd86099a6ba8f3d7 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 5 Sep 2025 10:25:38 +0100 Subject: [PATCH 3/6] Add test for trust situations. --- .../KeyStoragePanelViewModel-test.ts | 43 +++++++++++++++++-- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/test/unit-tests/components/viewmodels/settings/encryption/KeyStoragePanelViewModel-test.ts b/test/unit-tests/components/viewmodels/settings/encryption/KeyStoragePanelViewModel-test.ts index d0132c73179..fee28cf38f4 100644 --- a/test/unit-tests/components/viewmodels/settings/encryption/KeyStoragePanelViewModel-test.ts +++ b/test/unit-tests/components/viewmodels/settings/encryption/KeyStoragePanelViewModel-test.ts @@ -10,7 +10,7 @@ import { act } from "react"; import { mocked } from "jest-mock"; import type { MatrixClient } from "matrix-js-sdk/src/matrix"; -import type { KeyBackupCheck, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api"; +import type { BackupTrustInfo, KeyBackupCheck, KeyBackupInfo } from "matrix-js-sdk/src/crypto-api"; import { useKeyStoragePanelViewModel } from "../../../../../../src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel"; import { createTestClient, withClientContextRenderOptions } from "../../../../../test-utils"; @@ -49,8 +49,21 @@ describe("KeyStoragePanelViewModel", () => { expect(mocked(matrixClient.getCrypto()!.resetKeyBackup)).toHaveBeenCalled(); }); - it("should not call resetKeyBackup if there is a backup currently", async () => { - mocked(matrixClient.getCrypto()!.checkKeyBackupAndEnable).mockResolvedValue({} as KeyBackupCheck); + it.each([ + { trusted: true, matchesDecryptionKey: false }, + { trusted: false, matchesDecryptionKey: true }, + { trusted: true, matchesDecryptionKey: true }, + ])("should not call resetKeyBackup if there is a backup currently and it is trusted", async (trustInfo) => { + mocked(matrixClient.getCrypto()!.checkKeyBackupAndEnable).mockResolvedValue({ + backupInfo: { + version: "1", + algorithm: "foobar", + auth_data: { + public_key: "foobar", + }, + }, + trustInfo, + }); const { result } = renderHook( () => useKeyStoragePanelViewModel(), @@ -61,6 +74,30 @@ describe("KeyStoragePanelViewModel", () => { expect(mocked(matrixClient.getCrypto()!.resetKeyBackup)).not.toHaveBeenCalled(); }); + it("should call resetKeyBackup if there is a backup currently but it is not trusted", async () => { + mocked(matrixClient.getCrypto()!.checkKeyBackupAndEnable).mockResolvedValue({ + backupInfo: { + version: "1", + algorithm: "foobar", + auth_data: { + public_key: "foobar", + }, + }, + trustInfo: { + trusted: false, + matchesDecryptionKey: false, + }, + }); + + const { result } = renderHook( + () => useKeyStoragePanelViewModel(), + withClientContextRenderOptions(matrixClient), + ); + + await result.current.setEnabled(true); + expect(mocked(matrixClient.getCrypto()!.resetKeyBackup)).toHaveBeenCalled(); + }); + it("should set account data flag when enabling", async () => { mocked(matrixClient.getCrypto()!.checkKeyBackupAndEnable).mockResolvedValue(null); From bfecddcff4708d479ae889c36b77936deae23986 Mon Sep 17 00:00:00 2001 From: Half-Shot Date: Fri, 5 Sep 2025 10:27:45 +0100 Subject: [PATCH 4/6] remove conditional --- .../viewmodels/settings/encryption/KeyStoragePanelViewModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts index 1c24f2dd365..14f7e84a3c0 100644 --- a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts +++ b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts @@ -87,7 +87,7 @@ export function useKeyStoragePanelViewModel(): KeyStoragePanelState { currentKeyBackup.trustInfo, ); // Check if the current key backup can be used. Either of these properties causes the key backup to be used. - if (currentKeyBackup?.trustInfo.trusted || currentKeyBackup.trustInfo.matchesDecryptionKey) { + if (currentKeyBackup.trustInfo.trusted || currentKeyBackup.trustInfo.matchesDecryptionKey) { logger.info("Existing key backup can be used"); } else { logger.warn("Existing key backup cannot be used, creating new backup"); From 10bc52542a5335abd82ed4730da7567c36c53dd8 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 12 Sep 2025 17:46:15 +0100 Subject: [PATCH 5/6] Update src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- .../settings/encryption/KeyStoragePanelViewModel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts index 14f7e84a3c0..89a1ee21aae 100644 --- a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts +++ b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts @@ -98,8 +98,8 @@ export function useKeyStoragePanelViewModel(): KeyStoragePanelState { logger.info("No existing key backup versions are present, creating new backup"); } - // If there is no existing key backup on the server, create one. - // `resetKeyBackup` will delete any existing backup, so we only do this if there is no existing backup. + // If there is no usable key backup on the server, create one. + // `resetKeyBackup` will delete any existing backup, so we only do this if there is no usable backup. if (currentKeyBackup === null) { await crypto.resetKeyBackup(); // resetKeyBackup fires this off in the background without waiting, so we need to do it From c710ed9d75de8efb263ab91bd229f1e70947ac8d Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Fri, 12 Sep 2025 17:46:38 +0100 Subject: [PATCH 6/6] Update src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts Co-authored-by: Richard van der Hoff <1389908+richvdh@users.noreply.github.com> --- .../viewmodels/settings/encryption/KeyStoragePanelViewModel.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts index 89a1ee21aae..60ad21236f9 100644 --- a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts +++ b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts @@ -79,7 +79,8 @@ export function useKeyStoragePanelViewModel(): KeyStoragePanelState { return; } if (enable) { - logger.info("User requested enabling key backup"); + const childLogger = logger.getChild("[enable key storage]"); + childLogger.info("User requested enabling key storage"); let currentKeyBackup = await crypto.checkKeyBackupAndEnable(); if (currentKeyBackup) { logger.info(