Skip to content

Commit 3d56aa7

Browse files
authored
Fix logic in DeviceListener (#30230)
* remove incorrect check for cross-signing SETUP_ENCRYPTION tries to set up everything (4S, cross-signing and key backup), rather than just setting up encryption, as its name would imply. crossSigningReady == false happens when the user's device isn't verified, so it should trigger VERIFY_THIS_SESSION rather than SETUP_ENCRYPTION * reorder conditions in allSystemsReady to match the order in the if statements * explicitly handle secrets missing from 4S rather than falling back to the SETUP_ENCRYPTION catch-all. Also, remove SETUP_ENCRYPTION since it is no longer used. * convert button handlers to switch statements for consistency (almost) all the other functions that use make decisions based on Kind use switch statements * update i18n (remove obsolete string)
1 parent 58875e5 commit 3d56aa7

File tree

5 files changed

+176
-93
lines changed

5 files changed

+176
-93
lines changed

src/DeviceListener.ts

Lines changed: 8 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ export default class DeviceListener {
362362
// said we are OK with that.
363363
const keyBackupIsOk = keyBackupUploadActive || backupDisabled;
364364

365-
const allSystemsReady = crossSigningReady && keyBackupIsOk && recoveryIsOk && allCrossSigningSecretsCached;
365+
const allSystemsReady = isCurrentDeviceTrusted && allCrossSigningSecretsCached && keyBackupIsOk && recoveryIsOk;
366366

367367
await this.reportCryptoSessionStateToAnalytics(cli);
368368

@@ -375,13 +375,8 @@ export default class DeviceListener {
375375
// make sure our keys are finished downloading
376376
await crypto.getUserDeviceInfo([cli.getSafeUserId()]);
377377

378-
if (!crossSigningReady) {
379-
// This account is legacy and doesn't have cross-signing set up at all.
380-
// Prompt the user to set it up.
381-
logSpan.info("Cross-signing not ready: showing SET_UP_ENCRYPTION toast");
382-
showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION);
383-
} else if (!isCurrentDeviceTrusted) {
384-
// cross signing is ready but the current device is not trusted: prompt the user to verify
378+
if (!isCurrentDeviceTrusted) {
379+
// the current device is not trusted: prompt the user to verify
385380
logSpan.info("Current device not verified: showing VERIFY_THIS_SESSION toast");
386381
showSetupEncryptionToast(SetupKind.VERIFY_THIS_SESSION);
387382
} else if (!allCrossSigningSecretsCached) {
@@ -410,16 +405,11 @@ export default class DeviceListener {
410405
hideSetupEncryptionToast();
411406
}
412407
} else {
413-
// some other condition... yikes! Show the 'set up encryption' toast: this is what we previously did
414-
// in 'other' situations. Possibly we should consider prompting for a full reset in this case?
415-
logSpan.warn("Couldn't match encryption state to a known case: showing 'setup encryption' prompt", {
416-
crossSigningReady,
417-
secretStorageReady,
418-
allCrossSigningSecretsCached,
419-
isCurrentDeviceTrusted,
420-
defaultKeyId,
421-
});
422-
showSetupEncryptionToast(SetupKind.SET_UP_ENCRYPTION);
408+
// If we get here, then we are verified, have key backup, and
409+
// 4S, but crypto.isCrossSigningReady returned false, which
410+
// means that 4S doesn't have all the secrets.
411+
logSpan.warn("4S is missing secrets");
412+
showSetupEncryptionToast(SetupKind.KEY_STORAGE_OUT_OF_SYNC_STORE);
423413
}
424414
} else {
425415
logSpan.info("Not yet ready, but shouldShowSetupEncryptionToast==false");

src/i18n/strings/en_EN.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,6 @@
969969
"reset_all_button": "Forgotten or lost all recovery methods? <a>Reset all</a>",
970970
"set_up_recovery": "Set up recovery",
971971
"set_up_recovery_toast_description": "Generate a recovery key that can be used to restore your encrypted message history in case you lose access to your devices.",
972-
"set_up_toast_description": "Safeguard against losing access to encrypted messages & data",
973972
"set_up_toast_title": "Set up Secure Backup",
974973
"setup_secure_backup": {
975974
"explainer": "Back up your keys before signing out to avoid losing them."

src/toasts/SetupEncryptionToast.ts

Lines changed: 97 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,12 @@ const TOAST_KEY = "setupencryption";
3030

3131
const getTitle = (kind: Kind): string => {
3232
switch (kind) {
33-
case Kind.SET_UP_ENCRYPTION:
34-
return _t("encryption|set_up_toast_title");
3533
case Kind.SET_UP_RECOVERY:
3634
return _t("encryption|set_up_recovery");
3735
case Kind.VERIFY_THIS_SESSION:
3836
return _t("encryption|verify_toast_title");
3937
case Kind.KEY_STORAGE_OUT_OF_SYNC:
38+
case Kind.KEY_STORAGE_OUT_OF_SYNC_STORE:
4039
return _t("encryption|key_storage_out_of_sync");
4140
case Kind.TURN_ON_KEY_STORAGE:
4241
return _t("encryption|turn_on_key_storage");
@@ -45,12 +44,11 @@ const getTitle = (kind: Kind): string => {
4544

4645
const getIcon = (kind: Kind): string | undefined => {
4746
switch (kind) {
48-
case Kind.SET_UP_ENCRYPTION:
49-
return "secure_backup";
5047
case Kind.SET_UP_RECOVERY:
5148
return undefined;
5249
case Kind.VERIFY_THIS_SESSION:
5350
case Kind.KEY_STORAGE_OUT_OF_SYNC:
51+
case Kind.KEY_STORAGE_OUT_OF_SYNC_STORE:
5452
return "verification_warning";
5553
case Kind.TURN_ON_KEY_STORAGE:
5654
return "key_storage";
@@ -59,13 +57,12 @@ const getIcon = (kind: Kind): string | undefined => {
5957

6058
const getSetupCaption = (kind: Kind): string => {
6159
switch (kind) {
62-
case Kind.SET_UP_ENCRYPTION:
63-
return _t("action|continue");
6460
case Kind.SET_UP_RECOVERY:
6561
return _t("action|continue");
6662
case Kind.VERIFY_THIS_SESSION:
6763
return _t("action|verify");
6864
case Kind.KEY_STORAGE_OUT_OF_SYNC:
65+
case Kind.KEY_STORAGE_OUT_OF_SYNC_STORE:
6966
return _t("encryption|enter_recovery_key");
7067
case Kind.TURN_ON_KEY_STORAGE:
7168
return _t("action|continue");
@@ -79,6 +76,7 @@ const getSetupCaption = (kind: Kind): string => {
7976
const getPrimaryButtonIcon = (kind: Kind): ComponentType<React.SVGAttributes<SVGElement>> | undefined => {
8077
switch (kind) {
8178
case Kind.KEY_STORAGE_OUT_OF_SYNC:
79+
case Kind.KEY_STORAGE_OUT_OF_SYNC_STORE:
8280
return KeyIcon;
8381
default:
8482
return;
@@ -89,10 +87,10 @@ const getSecondaryButtonLabel = (kind: Kind): string => {
8987
switch (kind) {
9088
case Kind.SET_UP_RECOVERY:
9189
return _t("action|dismiss");
92-
case Kind.SET_UP_ENCRYPTION:
9390
case Kind.VERIFY_THIS_SESSION:
9491
return _t("encryption|verification|unverified_sessions_toast_reject");
9592
case Kind.KEY_STORAGE_OUT_OF_SYNC:
93+
case Kind.KEY_STORAGE_OUT_OF_SYNC_STORE:
9694
return _t("encryption|forgot_recovery_key");
9795
case Kind.TURN_ON_KEY_STORAGE:
9896
return _t("action|dismiss");
@@ -101,13 +99,12 @@ const getSecondaryButtonLabel = (kind: Kind): string => {
10199

102100
const getDescription = (kind: Kind): string => {
103101
switch (kind) {
104-
case Kind.SET_UP_ENCRYPTION:
105-
return _t("encryption|set_up_toast_description");
106102
case Kind.SET_UP_RECOVERY:
107103
return _t("encryption|set_up_recovery_toast_description");
108104
case Kind.VERIFY_THIS_SESSION:
109105
return _t("encryption|verify_toast_description");
110106
case Kind.KEY_STORAGE_OUT_OF_SYNC:
107+
case Kind.KEY_STORAGE_OUT_OF_SYNC_STORE:
111108
return _t("encryption|key_storage_out_of_sync_description");
112109
case Kind.TURN_ON_KEY_STORAGE:
113110
return _t("encryption|turn_on_key_storage_description");
@@ -118,10 +115,6 @@ const getDescription = (kind: Kind): string => {
118115
* The kind of toast to show.
119116
*/
120117
export enum Kind {
121-
/**
122-
* Prompt the user to set up encryption
123-
*/
124-
SET_UP_ENCRYPTION = "set_up_encryption",
125118
/**
126119
* Prompt the user to set up a recovery key
127120
*/
@@ -131,9 +124,13 @@ export enum Kind {
131124
*/
132125
VERIFY_THIS_SESSION = "verify_this_session",
133126
/**
134-
* Prompt the user to enter their recovery key
127+
* Prompt the user to enter their recovery key, to retrieve secrets
135128
*/
136129
KEY_STORAGE_OUT_OF_SYNC = "key_storage_out_of_sync",
130+
/**
131+
* Prompt the user to enter their recovery key, to store secrets
132+
*/
133+
KEY_STORAGE_OUT_OF_SYNC_STORE = "key_storage_out_of_sync_store",
137134
/**
138135
* Prompt the user to turn on key storage
139136
*/
@@ -156,77 +153,115 @@ export const showToast = (kind: Kind): void => {
156153
}
157154

158155
const onPrimaryClick = async (): Promise<void> => {
159-
if (kind === Kind.VERIFY_THIS_SESSION) {
160-
Modal.createDialog(SetupEncryptionDialog, {}, undefined, /* priority = */ false, /* static = */ true);
161-
} else if (kind == Kind.TURN_ON_KEY_STORAGE) {
162-
// Open the user settings dialog to the encryption tab
163-
const payload: OpenToTabPayload = {
164-
action: Action.ViewUserSettings,
165-
initialTabId: UserTab.Encryption,
166-
};
167-
defaultDispatcher.dispatch(payload);
168-
} else {
169-
const modal = Modal.createDialog(
170-
Spinner,
171-
undefined,
172-
"mx_Dialog_spinner",
173-
/* priority */ false,
174-
/* static */ true,
175-
);
176-
try {
177-
await accessSecretStorage();
178-
} catch (error) {
179-
onAccessSecretStorageFailed(error as Error);
180-
} finally {
181-
modal.close();
156+
switch (kind) {
157+
case Kind.TURN_ON_KEY_STORAGE: {
158+
// Open the user settings dialog to the encryption tab
159+
const payload: OpenToTabPayload = {
160+
action: Action.ViewUserSettings,
161+
initialTabId: UserTab.Encryption,
162+
};
163+
defaultDispatcher.dispatch(payload);
164+
break;
165+
}
166+
case Kind.VERIFY_THIS_SESSION:
167+
Modal.createDialog(SetupEncryptionDialog, {}, undefined, /* priority = */ false, /* static = */ true);
168+
break;
169+
case Kind.SET_UP_RECOVERY:
170+
case Kind.KEY_STORAGE_OUT_OF_SYNC:
171+
case Kind.KEY_STORAGE_OUT_OF_SYNC_STORE: {
172+
const modal = Modal.createDialog(
173+
Spinner,
174+
undefined,
175+
"mx_Dialog_spinner",
176+
/* priority */ false,
177+
/* static */ true,
178+
);
179+
try {
180+
await accessSecretStorage();
181+
} catch (error) {
182+
onAccessSecretStorageFailed(kind, error as Error);
183+
} finally {
184+
modal.close();
185+
}
186+
break;
182187
}
183188
}
184189
};
185190

186191
const onSecondaryClick = async (): Promise<void> => {
187-
if (kind === Kind.KEY_STORAGE_OUT_OF_SYNC) {
188-
// Open the user settings dialog to the encryption tab and start the flow to reset encryption
189-
const payload: OpenToTabPayload = {
190-
action: Action.ViewUserSettings,
191-
initialTabId: UserTab.Encryption,
192-
props: { initialEncryptionState: "reset_identity_forgot" },
193-
};
194-
defaultDispatcher.dispatch(payload);
195-
} else if (kind === Kind.TURN_ON_KEY_STORAGE) {
196-
// The user clicked "Dismiss": offer them "Are you sure?"
197-
const modal = Modal.createDialog(ConfirmKeyStorageOffDialog, undefined, "mx_ConfirmKeyStorageOffDialog");
198-
const [dismissed] = await modal.finished;
199-
if (dismissed) {
192+
switch (kind) {
193+
case Kind.SET_UP_RECOVERY: {
194+
// Record that the user doesn't want to set up recovery
200195
const deviceListener = DeviceListener.sharedInstance();
201-
await deviceListener.recordKeyBackupDisabled();
196+
await deviceListener.recordRecoveryDisabled();
202197
deviceListener.dismissEncryptionSetup();
198+
break;
203199
}
204-
} else if (kind === Kind.SET_UP_RECOVERY) {
205-
// Record that the user doesn't want to set up recovery
206-
const deviceListener = DeviceListener.sharedInstance();
207-
await deviceListener.recordRecoveryDisabled();
208-
deviceListener.dismissEncryptionSetup();
209-
} else {
210-
DeviceListener.sharedInstance().dismissEncryptionSetup();
200+
case Kind.KEY_STORAGE_OUT_OF_SYNC: {
201+
// Open the user settings dialog to the encryption tab and start the flow to reset encryption
202+
const payload: OpenToTabPayload = {
203+
action: Action.ViewUserSettings,
204+
initialTabId: UserTab.Encryption,
205+
props: { initialEncryptionState: "reset_identity_forgot" },
206+
};
207+
defaultDispatcher.dispatch(payload);
208+
break;
209+
}
210+
case Kind.KEY_STORAGE_OUT_OF_SYNC_STORE: {
211+
// Open the user settings dialog to the encryption tab and start the flow to reset 4S
212+
const payload: OpenToTabPayload = {
213+
action: Action.ViewUserSettings,
214+
initialTabId: UserTab.Encryption,
215+
props: { initialEncryptionState: "change_recovery_key" },
216+
};
217+
defaultDispatcher.dispatch(payload);
218+
break;
219+
}
220+
case Kind.TURN_ON_KEY_STORAGE: {
221+
// The user clicked "Dismiss": offer them "Are you sure?"
222+
const modal = Modal.createDialog(
223+
ConfirmKeyStorageOffDialog,
224+
undefined,
225+
"mx_ConfirmKeyStorageOffDialog",
226+
);
227+
const [dismissed] = await modal.finished;
228+
if (dismissed) {
229+
const deviceListener = DeviceListener.sharedInstance();
230+
await deviceListener.recordKeyBackupDisabled();
231+
deviceListener.dismissEncryptionSetup();
232+
}
233+
break;
234+
}
235+
default:
236+
DeviceListener.sharedInstance().dismissEncryptionSetup();
211237
}
212238
};
213239

214240
/**
215241
* We tried to accessSecretStorage, which triggered us to ask for the
216242
* recovery key, but this failed. If the user just gave up, that is fine,
217243
* but if not, that means downloading encryption info from 4S did not fix
218-
* the problem we identified. Presumably, something is wrong with what
219-
* they have in 4S: we tell them to reset their identity.
244+
* the problem we identified. Presumably, something is wrong with what they
245+
* have in 4S. If we were trying to fetch secrets from 4S, we tell them to
246+
* reset their identity, to reset everything. If we were trying to store
247+
* secrets in 4S, or set up recovery, we tell them to change their recovery
248+
* key, to create a new 4S that we can store the secrets in.
220249
*/
221-
const onAccessSecretStorageFailed = (error: Error): void => {
250+
const onAccessSecretStorageFailed = (
251+
kind: Kind.SET_UP_RECOVERY | Kind.KEY_STORAGE_OUT_OF_SYNC | Kind.KEY_STORAGE_OUT_OF_SYNC_STORE,
252+
error: Error,
253+
): void => {
222254
if (error instanceof AccessCancelledError) {
223255
// The user cancelled the dialog - just allow it to close
224256
} else {
225257
// A real error happened - jump to the reset identity tab
226258
const payload: OpenToTabPayload = {
227259
action: Action.ViewUserSettings,
228260
initialTabId: UserTab.Encryption,
229-
props: { initialEncryptionState: "reset_identity_sync_failed" },
261+
props: {
262+
initialEncryptionState:
263+
kind === Kind.KEY_STORAGE_OUT_OF_SYNC ? "reset_identity_sync_failed" : "change_recovery_key",
264+
},
230265
};
231266
defaultDispatcher.dispatch(payload);
232267
}

0 commit comments

Comments
 (0)