From d9d752804cf066b2437186cbe113497abd320f92 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 8 Jan 2025 17:22:15 +0000 Subject: [PATCH] Very early WIP of rejigged e2e error toast code --- src/DeviceListener.ts | 57 ++++++++++++++---------------- src/i18n/strings/en_EN.json | 4 +++ src/toasts/SetupEncryptionToast.ts | 12 +++++++ 3 files changed, 43 insertions(+), 30 deletions(-) diff --git a/src/DeviceListener.ts b/src/DeviceListener.ts index 28bb5f655e..3a2d7b1db8 100644 --- a/src/DeviceListener.ts +++ b/src/DeviceListener.ts @@ -283,7 +283,19 @@ 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 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,22 @@ 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 cached. + // prompt the user to enter their recovery key. + showSetupEncryptionToast(SetupKind.KEY_STORAGE_OUT_OF_SYNC); + } else if ((await cli.secretStorage.getDefaultKeyId()) === 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! } } @@ -334,12 +337,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/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 331598c5b4..3318344e82 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -882,14 +882,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..774255fdd5 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"); } }; @@ -49,6 +51,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 +63,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 +76,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 +97,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 => {