From 58f812ffe631015e79b0a20afcf507919449203c Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 16 Jan 2025 13:17:18 +0000 Subject: [PATCH] Add toast for recovery keys being out of sync (#28946) * Refine `SettingsSection` & `SettingsTab` * Add encryption tab * Add recovery section * Add device verification * Rename `Panel` into `State` * Update & add tests to user settings common * Add tests to `RecoveryPanel` * Add tests to `ChangeRecoveryKey` * Update CreateSecretStorageDialog-test snapshot * Add tests to `EncryptionUserSettingsTab` * Update existing screenshots of e2e tests * Add new encryption tab ownership to `@element-hq/element-crypto-web-reviewers` * Add e2e tests * Fix monospace font and add figma link to hardcoded value * Add unit to Icon * Improve e2e doc * Assert that the crypto module is defined * Add classname doc * Fix typo * Use `good` state instead of default * Rename `ChangeRecoveryKey.isSetupFlow` into `ChangeRecoveryKey.userHasKeyBackup` * Move `deleteCachedSecrets` fixture in `recovery.spec.ts` * Use one callback instead of two in `RecoveryPanel` * Fix docs and naming of `utils.createBot` * Fix typo in `RecoveryPanel` * Add more doc to the state of the `EncryptionUserSettingsTab` * Rename `verification_required` into `set_up_encryption` * Update test * ADd new license * Very early WIP of rejigged e2e error toast code * Update comments and doc * Assert that `recoveryKey.encodedPrivateKey` is always defined * Add comments to explain how the secrets could be uncached * Use `matrixClient.secretStorage.getDefaultKeyId` instead of `matrixClient.getCrypto().checkKeyBackupAndEnable` to know if we need to set up a recovery key * Update existing screenshot to add encryption tab. * Fix tests * Remove unused file! * Remove test for unused file * Show 'set up encryption' in the 'other' case. * Test 'key storage out of sync' toast * Update tests * Fix test & make toast look correct * Use new labels when changing the recovery key * Fix docs * Don't reset key backup when creating a recovery key * Add playwright test for toast * Dismiss the toast as it's now in the way due to being wider * Doesn't look like this needs to be async * Typo Co-authored-by: Andy Balaam * Typo Co-authored-by: Andy Balaam * Override width for just this toast --------- Co-authored-by: Florian Duros Co-authored-by: Florian Duros Co-authored-by: Andy Balaam --- playwright/e2e/crypto/event-shields.spec.ts | 3 + playwright/e2e/crypto/utils.ts | 22 ++++++ playwright/e2e/room/room-header.spec.ts | 4 + .../encryption-user-tab/recovery.spec.ts | 24 +----- .../key-storage-out-of-sync-toast-linux.png | Bin 0 -> 19162 bytes src/DeviceListener.ts | 72 ++++++++++-------- src/components/views/toasts/GenericToast.tsx | 6 +- src/i18n/strings/en_EN.json | 4 + src/toasts/SetupEncryptionToast.ts | 14 ++++ src/utils/login.ts | 17 ----- test/unit-tests/DeviceListener-test.ts | 19 ++--- .../toasts/SetupEncryptionToast-test.tsx | 8 +- test/unit-tests/utils/login-test.ts | 22 ------ 13 files changed, 109 insertions(+), 106 deletions(-) create mode 100644 playwright/snapshots/crypto/toasts.spec.ts/key-storage-out-of-sync-toast-linux.png delete mode 100644 src/utils/login.ts delete mode 100644 test/unit-tests/utils/login-test.ts diff --git a/playwright/e2e/crypto/event-shields.spec.ts b/playwright/e2e/crypto/event-shields.spec.ts index 3811c2819e..c0f1e280a2 100644 --- a/playwright/e2e/crypto/event-shields.spec.ts +++ b/playwright/e2e/crypto/event-shields.spec.ts @@ -66,6 +66,9 @@ test.describe("Cryptography", function () { // Bob has a second, not cross-signed, device const bobSecondDevice = await createSecondBotDevice(page, homeserver, bob); + // Dismiss the toast nagging us to set up recovery otherwise it gets in the way of clicking the room list + await page.getByRole("button", { name: "Not now" }).click(); + await bob.sendEvent(testRoomId, null, "m.room.encrypted", { algorithm: "m.megolm.v1.aes-sha2", ciphertext: "the bird is in the hand", diff --git a/playwright/e2e/crypto/utils.ts b/playwright/e2e/crypto/utils.ts index c880961964..7474c5a435 100644 --- a/playwright/e2e/crypto/utils.ts +++ b/playwright/e2e/crypto/utils.ts @@ -413,3 +413,25 @@ export async function createSecondBotDevice(page: Page, homeserver: HomeserverIn await bobSecondDevice.prepareClient(); return bobSecondDevice; } + +/** + * Remove the cached secrets from the indexedDB + * This is a workaround to simulate the case where the secrets are not cached. + */ +export async function deleteCachedSecrets(page: Page) { + await page.evaluate(async () => { + const removeCachedSecrets = new Promise((resolve) => { + const request = window.indexedDB.open("matrix-js-sdk::matrix-sdk-crypto"); + request.onsuccess = (event: Event & { target: { result: IDBDatabase } }) => { + const db = event.target.result; + const request = db.transaction("core", "readwrite").objectStore("core").delete("private_identity"); + request.onsuccess = () => { + db.close(); + resolve(undefined); + }; + }; + }); + await removeCachedSecrets; + }); + await page.reload(); +} diff --git a/playwright/e2e/room/room-header.spec.ts b/playwright/e2e/room/room-header.spec.ts index 7fe0cb3d47..f19bd68f14 100644 --- a/playwright/e2e/room/room-header.spec.ts +++ b/playwright/e2e/room/room-header.spec.ts @@ -111,6 +111,10 @@ test.describe("Room Header", () => { async ({ page, app, user }) => { await createVideoRoom(page, app); + // Dismiss a toast that is otherwise in the way (it's the other + // side but there's no need to have it in the screenshot) + await page.getByRole("button", { name: "Later" }).click(); + const header = page.locator(".mx_RoomHeader"); // There's two room info button - the header itself and the i button diff --git a/playwright/e2e/settings/encryption-user-tab/recovery.spec.ts b/playwright/e2e/settings/encryption-user-tab/recovery.spec.ts index e6812cd450..a322d42d4e 100644 --- a/playwright/e2e/settings/encryption-user-tab/recovery.spec.ts +++ b/playwright/e2e/settings/encryption-user-tab/recovery.spec.ts @@ -6,13 +6,13 @@ */ import { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api"; -import { Page } from "@playwright/test"; import { test, expect } from "."; import { checkDeviceIsConnectedKeyBackup, checkDeviceIsCrossSigned, createBot, + deleteCachedSecrets, verifySession, } from "../../crypto/utils"; @@ -154,25 +154,3 @@ test.describe("Recovery section in Encryption tab", () => { }, ); }); - -/** - * Remove the cached secrets from the indexedDB - * This is a workaround to simulate the case where the secrets are not cached. - */ -async function deleteCachedSecrets(page: Page) { - await page.evaluate(async () => { - const removeCachedSecrets = new Promise((resolve) => { - const request = window.indexedDB.open("matrix-js-sdk::matrix-sdk-crypto"); - request.onsuccess = async (event: Event & { target: { result: IDBDatabase } }) => { - const db = event.target.result; - const request = db.transaction("core", "readwrite").objectStore("core").delete("private_identity"); - request.onsuccess = () => { - db.close(); - resolve(undefined); - }; - }; - }); - await removeCachedSecrets; - }); - await page.reload(); -} diff --git a/playwright/snapshots/crypto/toasts.spec.ts/key-storage-out-of-sync-toast-linux.png b/playwright/snapshots/crypto/toasts.spec.ts/key-storage-out-of-sync-toast-linux.png new file mode 100644 index 0000000000000000000000000000000000000000..8e335bd2323b56429f8b03fdbcefd5ed77e9d6f2 GIT binary patch literal 19162 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 literal 0 HcmV?d00001 diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index 28bb5f655e..e50f0d3f9b 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -34,11 +34,9 @@ import { hideToast as hideUnverifiedSessionsToast, showToast as showUnverifiedSessionsToast, } from "./toasts/UnverifiedSessionToast"; -import { accessSecretStorage, isSecretStorageBeingAccessed } from "./SecurityManager"; -import { isSecureBackupRequired } from "./utils/WellKnownUtils"; +import { isSecretStorageBeingAccessed } from "./SecurityManager"; import { ActionPayload } from "./dispatcher/payloads"; import { Action } from "./dispatcher/actions"; -import { isLoggedIn } from "./utils/login"; import SdkConfig from "./SdkConfig"; import PlatformPeg from "./PlatformPeg"; import { recordClientInformation, removeClientInformation } from "./utils/device/clientInformation"; @@ -283,7 +281,21 @@ export default class DeviceListener { const crossSigningReady = await crypto.isCrossSigningReady(); const secretStorageReady = await crypto.isSecretStorageReady(); - const allSystemsReady = crossSigningReady && secretStorageReady; + const crossSigningStatus = await crypto.getCrossSigningStatus(); + const allCrossSigningSecretsCached = + crossSigningStatus.privateKeysCachedLocally.masterKey && + crossSigningStatus.privateKeysCachedLocally.selfSigningKey && + crossSigningStatus.privateKeysCachedLocally.userSigningKey; + + const defaultKeyId = await cli.secretStorage.getDefaultKeyId(); + + const isCurrentDeviceTrusted = + crossSigningReady && + Boolean( + (await crypto.getDeviceVerificationStatus(cli.getSafeUserId(), cli.deviceId!))?.crossSigningVerified, + ); + + const allSystemsReady = crossSigningReady && secretStorageReady && allCrossSigningSecretsCached; await this.reportCryptoSessionStateToAnalytics(cli); if (this.dismissedThisDeviceToast || allSystemsReady) { @@ -294,31 +306,31 @@ export default class DeviceListener { // make sure our keys are finished downloading await crypto.getUserDeviceInfo([cli.getSafeUserId()]); - // cross signing isn't enabled - nag to enable it - // There are 3 different toasts for: - if (!(await crypto.getCrossSigningKeyId()) && (await crypto.userHasCrossSigningKeys())) { - // Toast 1. Cross-signing on account but this device doesn't trust the master key (verify this session) + if (!crossSigningReady) { + // This account is legacy and doesn't have cross-signing set up at all. + // Prompt the user to set it up. + showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION); + } else if (!isCurrentDeviceTrusted) { + // cross signing is ready but the current device is not trusted: prompt the user to verify showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION); - this.checkKeyBackupStatus(); + } else if (!allCrossSigningSecretsCached) { + // cross signing ready & device trusted, but we are missing secrets from our local cache. + // prompt the user to enter their recovery key. + showSetupEncryptionToast(SetupKind.KEY_STORAGE_OUT_OF_SYNC); + } else if (defaultKeyId === null) { + // the user just hasn't set up 4S yet: prompt them to do so + showSetupEncryptionToast(SetupKind.SET_UP_RECOVERY); } else { - const backupInfo = await this.getKeyBackupInfo(); - if (backupInfo) { - // Toast 2: Key backup is enabled but recovery (4S) is not set up: prompt user to set up recovery. - // Since we now enable key backup at registration time, this will be the common case for - // new users. - showSetupEncryptionToast(SetupKind.SET_UP_RECOVERY); - } else { - // Toast 3: No cross-signing or key backup on account (set up encryption) - await cli.waitForClientWellKnown(); - if (isSecureBackupRequired(cli) && isLoggedIn()) { - // If we're meant to set up, and Secure Backup is required, - // trigger the flow directly without a toast once logged in. - hideSetupEncryptionToast(); - accessSecretStorage(); - } else { - showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION); - } - } + // some other condition... yikes! Show the 'set up encryption' toast: this is what we previously did + // in 'other' situations. Possibly we should consider prompting for a full reset in this case? + logger.warn("Couldn't match encryption state to a known case: showing 'setup encryption' prompt", { + crossSigningReady, + secretStorageReady, + allCrossSigningSecretsCached, + isCurrentDeviceTrusted, + defaultKeyId, + }); + showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION); } } @@ -334,12 +346,6 @@ export default class DeviceListener { // Unverified devices that have appeared since then const newUnverifiedDeviceIds = new Set(); - const isCurrentDeviceTrusted = - crossSigningReady && - Boolean( - (await crypto.getDeviceVerificationStatus(cli.getSafeUserId(), cli.deviceId!))?.crossSigningVerified, - ); - // as long as cross-signing isn't ready, // you can't see or dismiss any device toasts if (crossSigningReady) { diff --git a/src/components/views/toasts/GenericToast.tsx b/src/components/views/toasts/GenericToast.tsx index 0e249cecdc..61c6272377 100644 --- a/src/components/views/toasts/GenericToast.tsx +++ b/src/components/views/toasts/GenericToast.tsx @@ -25,6 +25,9 @@ interface IPropsExtended extends IProps { SecondaryIcon?: ComponentType>; destructive?: "primary" | "secondary"; onSecondaryClick(): void; + + // If set, this will override the max-width (of the description) making the toast wider or narrower than standard + overrideWidth?: string; } const GenericToast: React.FC> = ({ @@ -37,12 +40,13 @@ const GenericToast: React.FC> = ({ destructive, onPrimaryClick, onSecondaryClick, + overrideWidth, }) => { const detailContent = detail ?
{detail}
: null; return (
-
+
{description} {detailContent}
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 37a739a62e..875da43f14 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -879,14 +879,18 @@ "title": "Destroy cross-signing keys?", "warning": "Deleting cross-signing keys is permanent. Anyone you have verified with will see security alerts. You almost certainly don't want to do this, unless you've lost every device you can cross-sign from." }, + "enter_recovery_key": "Enter recovery key", "event_shield_reason_authenticity_not_guaranteed": "The authenticity of this encrypted message can't be guaranteed on this device.", "event_shield_reason_mismatched_sender_key": "Encrypted by an unverified session", "event_shield_reason_unknown_device": "Encrypted by an unknown or deleted device.", "event_shield_reason_unsigned_device": "Encrypted by a device not verified by its owner.", "event_shield_reason_unverified_identity": "Encrypted by an unverified user.", "export_unsupported": "Your browser does not support the required cryptography extensions", + "forgot_recovery_key": "Forgot recovery key?", "import_invalid_keyfile": "Not a valid %(brand)s keyfile", "import_invalid_passphrase": "Authentication check failed: incorrect password?", + "key_storage_out_of_sync": "Your key storage is out of sync.", + "key_storage_out_of_sync_description": "Confirm your recovery key to maintain access to your key storage and message history.", "messages_not_secure": { "cause_1": "Your homeserver", "cause_2": "The homeserver the user you're verifying is connected to", diff --git a/src/toasts/SetupEncryptionToast.ts b/src/toasts/SetupEncryptionToast.ts index ecbf99f4b2..3b8e85eb44 100644 --- a/src/toasts/SetupEncryptionToast.ts +++ b/src/toasts/SetupEncryptionToast.ts @@ -27,6 +27,8 @@ const getTitle = (kind: Kind): string => { return _t("encryption|set_up_recovery"); case Kind.VERIFY_THIS_SESSION: return _t("encryption|verify_toast_title"); + case Kind.KEY_STORAGE_OUT_OF_SYNC: + return _t("encryption|key_storage_out_of_sync"); } }; @@ -37,6 +39,7 @@ const getIcon = (kind: Kind): string | undefined => { case Kind.SET_UP_RECOVERY: return undefined; case Kind.VERIFY_THIS_SESSION: + case Kind.KEY_STORAGE_OUT_OF_SYNC: return "verification_warning"; } }; @@ -49,6 +52,8 @@ const getSetupCaption = (kind: Kind): string => { return _t("action|continue"); case Kind.VERIFY_THIS_SESSION: return _t("action|verify"); + case Kind.KEY_STORAGE_OUT_OF_SYNC: + return _t("encryption|enter_recovery_key"); } }; @@ -59,6 +64,8 @@ const getSecondaryButtonLabel = (kind: Kind): string => { case Kind.SET_UP_ENCRYPTION: case Kind.VERIFY_THIS_SESSION: return _t("encryption|verification|unverified_sessions_toast_reject"); + case Kind.KEY_STORAGE_OUT_OF_SYNC: + return _t("encryption|forgot_recovery_key"); } }; @@ -70,6 +77,8 @@ const getDescription = (kind: Kind): string => { return _t("encryption|set_up_recovery_toast_description"); case Kind.VERIFY_THIS_SESSION: return _t("encryption|verify_toast_description"); + case Kind.KEY_STORAGE_OUT_OF_SYNC: + return _t("encryption|key_storage_out_of_sync_description"); } }; @@ -89,6 +98,10 @@ export enum Kind { * Prompt the user to verify this session */ VERIFY_THIS_SESSION = "verify_this_session", + /** + * Prompt the user to enter their recovery key + */ + KEY_STORAGE_OUT_OF_SYNC = "key_storage_out_of_sync", } const onReject = (): void => { @@ -139,6 +152,7 @@ export const showToast = (kind: Kind): void => { onPrimaryClick: onAccept, secondaryLabel: getSecondaryButtonLabel(kind), onSecondaryClick: onReject, + overrideWidth: kind === Kind.KEY_STORAGE_OUT_OF_SYNC ? "366px" : undefined, }, component: GenericToast, priority: kind === Kind.VERIFY_THIS_SESSION ? 95 : 40, diff --git a/src/utils/login.ts b/src/utils/login.ts deleted file mode 100644 index 8f5d93ffae..0000000000 --- a/src/utils/login.ts +++ /dev/null @@ -1,17 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. -Copyright 2022 The Matrix.org Foundation C.I.C. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import Views from "../Views"; - -export function isLoggedIn(): boolean { - // JRS: Maybe we should move the step that writes this to the window out of - // `element-web` and into this file? Better yet, we should probably create a - // store to hold this state. - // See also https://github.com/vector-im/element-web/issues/15034. - return window.matrixChat?.state.view === Views.LOGGED_IN; -} diff --git a/test/unit-tests/DeviceListener-test.ts b/test/unit-tests/DeviceListener-test.ts index b63896c64d..bdbf86637d 100644 --- a/test/unit-tests/DeviceListener-test.ts +++ b/test/unit-tests/DeviceListener-test.ts @@ -329,7 +329,7 @@ describe("DeviceListener", () => { }); it("shows verify session toast when account has cross signing", async () => { - mockCrypto!.userHasCrossSigningKeys.mockResolvedValue(true); + mockCrypto!.isCrossSigningReady.mockResolvedValue(true); await createAndStart(); expect(mockCrypto!.getUserDeviceInfo).toHaveBeenCalled(); @@ -337,24 +337,25 @@ describe("DeviceListener", () => { SetupEncryptionToast.Kind.VERIFY_THIS_SESSION, ); }); - - it("checks key backup status when when account has cross signing", async () => { - mockCrypto!.getCrossSigningKeyId.mockResolvedValue(null); - mockCrypto!.userHasCrossSigningKeys.mockResolvedValue(true); - await createAndStart(); - - expect(mockCrypto!.getActiveSessionBackupVersion).toHaveBeenCalled(); - }); }); describe("when user does have a cross signing id on this device", () => { beforeEach(() => { + mockCrypto!.isCrossSigningReady.mockResolvedValue(true); mockCrypto!.getCrossSigningKeyId.mockResolvedValue("abc"); + mockCrypto!.getDeviceVerificationStatus.mockResolvedValue( + new DeviceVerificationStatus({ + trustCrossSignedDevices: true, + crossSigningVerified: true, + }), + ); }); it("shows set up recovery toast when user has a key backup available", async () => { // non falsy response mockCrypto.getKeyBackupInfo.mockResolvedValue({} as unknown as KeyBackupInfo); + mockClient.secretStorage.getDefaultKeyId.mockResolvedValue(null); + await createAndStart(); expect(SetupEncryptionToast.showToast).toHaveBeenCalledWith( diff --git a/test/unit-tests/toasts/SetupEncryptionToast-test.tsx b/test/unit-tests/toasts/SetupEncryptionToast-test.tsx index 8917587cc1..22b491817c 100644 --- a/test/unit-tests/toasts/SetupEncryptionToast-test.tsx +++ b/test/unit-tests/toasts/SetupEncryptionToast-test.tsx @@ -16,9 +16,15 @@ describe("SetupEncryptionToast", () => { render(); }); - it("should render the se up recovery toast", async () => { + it("should render the 'set up recovery' toast", async () => { showToast(Kind.SET_UP_RECOVERY); await expect(screen.findByText("Set up recovery")).resolves.toBeInTheDocument(); }); + + it("should render the 'key storage out of sync' toast", async () => { + showToast(Kind.KEY_STORAGE_OUT_OF_SYNC); + + await expect(screen.findByText("Your key storage is out of sync.")).resolves.toBeInTheDocument(); + }); }); diff --git a/test/unit-tests/utils/login-test.ts b/test/unit-tests/utils/login-test.ts deleted file mode 100644 index b1f488c29f..0000000000 --- a/test/unit-tests/utils/login-test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/* -Copyright 2024 New Vector Ltd. - -SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial -Please see LICENSE files in the repository root for full details. -*/ - -import MatrixChat from "../../../src/components/structures/MatrixChat.tsx"; -import { isLoggedIn } from "../../../src/utils/login.ts"; -import Views from "../../../src/Views.ts"; - -describe("isLoggedIn", () => { - it("should return true if MatrixChat state view is LOGGED_IN", () => { - window.matrixChat = { - state: { - view: Views.LOGGED_IN, - }, - } as unknown as MatrixChat; - - expect(isLoggedIn()).toBe(true); - }); -});