diff --git a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts index ee301bd27f..60ad21236f 100644 --- a/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts +++ b/src/components/viewmodels/settings/encryption/KeyStoragePanelViewModel.ts @@ -79,12 +79,30 @@ export function useKeyStoragePanelViewModel(): KeyStoragePanelState { return; } if (enable) { - // 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(); + const childLogger = logger.getChild("[enable key storage]"); + childLogger.info("User requested enabling key storage"); + 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 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 // explicitly and wait for it, otherwise it won't be enabled yet when we check again. await crypto.checkKeyBackupAndEnable(); @@ -93,6 +111,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(); 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 d0132c7317..fee28cf38f 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);