@@ -11,7 +11,7 @@ import React, { createRef } from "react";
11
11
import FileSaver from "file-saver" ;
12
12
import { logger } from "matrix-js-sdk/src/logger" ;
13
13
import { AuthDict , CrossSigningKeys , MatrixError , UIAFlow , UIAResponse } from "matrix-js-sdk/src/matrix" ;
14
- import { CryptoEvent , BackupTrustInfo , GeneratedSecretStorageKey , KeyBackupInfo } from "matrix-js-sdk/src/crypto-api" ;
14
+ import { GeneratedSecretStorageKey } from "matrix-js-sdk/src/crypto-api" ;
15
15
import classNames from "classnames" ;
16
16
import CheckmarkIcon from "@vector-im/compound-design-tokens/assets/web/icons/check" ;
17
17
@@ -25,7 +25,6 @@ import StyledRadioButton from "../../../../components/views/elements/StyledRadio
25
25
import AccessibleButton from "../../../../components/views/elements/AccessibleButton" ;
26
26
import DialogButtons from "../../../../components/views/elements/DialogButtons" ;
27
27
import InlineSpinner from "../../../../components/views/elements/InlineSpinner" ;
28
- import RestoreKeyBackupDialog from "../../../../components/views/dialogs/security/RestoreKeyBackupDialog" ;
29
28
import {
30
29
getSecureBackupSetupMethods ,
31
30
isSecureBackupRequired ,
@@ -45,7 +44,6 @@ enum Phase {
45
44
Loading = "loading" ,
46
45
LoadError = "load_error" ,
47
46
ChooseKeyPassphrase = "choose_key_passphrase" ,
48
- Migrate = "migrate" ,
49
47
Passphrase = "passphrase" ,
50
48
PassphraseConfirm = "passphrase_confirm" ,
51
49
ShowKey = "show_key" ,
@@ -72,24 +70,6 @@ interface IState {
72
70
downloaded : boolean ;
73
71
setPassphrase : boolean ;
74
72
75
- /** Information on the current key backup version, as returned by the server.
76
- *
77
- * `null` could mean any of:
78
- * * we haven't yet requested the data from the server.
79
- * * we were unable to reach the server.
80
- * * the server returned key backup version data we didn't understand or was malformed.
81
- * * there is actually no backup on the server.
82
- */
83
- backupInfo : KeyBackupInfo | null ;
84
-
85
- /**
86
- * Information on whether the backup in `backupInfo` is correctly signed, and whether we have the right key to
87
- * decrypt it.
88
- *
89
- * `undefined` if `backupInfo` is null, or if crypto is not enabled in the client.
90
- */
91
- backupTrustInfo : BackupTrustInfo | undefined ;
92
-
93
73
// does the server offer a UI auth flow with just m.login.password
94
74
// for /keys/device_signing/upload?
95
75
canUploadKeysWithPasswordOnly : boolean | null ;
@@ -141,16 +121,17 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
141
121
this . queryKeyUploadAuth ( ) ;
142
122
}
143
123
124
+ const keyFromCustomisations = ModuleRunner . instance . extensions . cryptoSetup . createSecretStorageKey ( ) ;
125
+ const phase = keyFromCustomisations ? Phase . Loading : Phase . ChooseKeyPassphrase ;
126
+
144
127
this . state = {
145
- phase : Phase . Loading ,
128
+ phase,
146
129
passPhrase : "" ,
147
130
passPhraseValid : false ,
148
131
passPhraseConfirm : "" ,
149
132
copied : false ,
150
133
downloaded : false ,
151
134
setPassphrase : false ,
152
- backupInfo : null ,
153
- backupTrustInfo : undefined ,
154
135
// does the server offer a UI auth flow with just m.login.password
155
136
// for /keys/device_signing/upload?
156
137
accountPasswordCorrect : null ,
@@ -160,60 +141,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
160
141
accountPassword,
161
142
} ;
162
143
163
- cli . on ( CryptoEvent . KeyBackupStatus , this . onKeyBackupStatusChange ) ;
164
-
165
- this . getInitialPhase ( ) ;
166
- }
167
-
168
- public componentWillUnmount ( ) : void {
169
- MatrixClientPeg . get ( ) ?. removeListener ( CryptoEvent . KeyBackupStatus , this . onKeyBackupStatusChange ) ;
170
- }
171
-
172
- private getInitialPhase ( ) : void {
173
- const keyFromCustomisations = ModuleRunner . instance . extensions . cryptoSetup . createSecretStorageKey ( ) ;
174
- if ( keyFromCustomisations ) {
175
- logger . log ( "CryptoSetupExtension: Created key via extension, jumping to bootstrap step" ) ;
176
- this . recoveryKey = {
177
- privateKey : keyFromCustomisations ,
178
- } ;
179
- this . bootstrapSecretStorage ( ) ;
180
- return ;
181
- }
182
-
183
- this . fetchBackupInfo ( ) ;
144
+ if ( keyFromCustomisations ) this . initExtension ( keyFromCustomisations ) ;
184
145
}
185
146
186
- /**
187
- * Attempt to get information on the current backup from the server, and update the state.
188
- *
189
- * Updates {@link IState.backupInfo} and {@link IState.backupTrustInfo}, and picks an appropriate phase for
190
- * {@link IState.phase}.
191
- *
192
- * @returns If the backup data was retrieved successfully, the trust info for the backup. Otherwise, undefined.
193
- */
194
- private async fetchBackupInfo ( ) : Promise < BackupTrustInfo | undefined > {
195
- try {
196
- const cli = MatrixClientPeg . safeGet ( ) ;
197
- const backupInfo = await cli . getKeyBackupVersion ( ) ;
198
- const backupTrustInfo =
199
- // we may not have started crypto yet, in which case we definitely don't trust the backup
200
- backupInfo ? await cli . getCrypto ( ) ?. isKeyBackupTrusted ( backupInfo ) : undefined ;
201
-
202
- const { forceReset } = this . props ;
203
- const phase = backupInfo && ! forceReset ? Phase . Migrate : Phase . ChooseKeyPassphrase ;
204
-
205
- this . setState ( {
206
- phase,
207
- backupInfo,
208
- backupTrustInfo,
209
- } ) ;
210
-
211
- return backupTrustInfo ;
212
- } catch ( e ) {
213
- console . error ( "Error fetching backup data from server" , e ) ;
214
- this . setState ( { phase : Phase . LoadError } ) ;
215
- return undefined ;
216
- }
147
+ private initExtension ( keyFromCustomisations : Uint8Array ) : void {
148
+ logger . log ( "CryptoSetupExtension: Created key via extension, jumping to bootstrap step" ) ;
149
+ this . recoveryKey = {
150
+ privateKey : keyFromCustomisations ,
151
+ } ;
152
+ this . bootstrapSecretStorage ( ) ;
217
153
}
218
154
219
155
private async queryKeyUploadAuth ( ) : Promise < void > {
@@ -237,10 +173,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
237
173
}
238
174
}
239
175
240
- private onKeyBackupStatusChange = ( ) : void => {
241
- if ( this . state . phase === Phase . Migrate ) this . fetchBackupInfo ( ) ;
242
- } ;
243
-
244
176
private onKeyPassphraseChange = ( e : React . ChangeEvent < HTMLInputElement > ) : void => {
245
177
this . setState ( {
246
178
passPhraseKeySelected : e . target . value ,
@@ -265,15 +197,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
265
197
}
266
198
} ;
267
199
268
- private onMigrateFormSubmit = ( e : React . FormEvent ) : void => {
269
- e . preventDefault ( ) ;
270
- if ( this . state . backupTrustInfo ?. trusted ) {
271
- this . bootstrapSecretStorage ( ) ;
272
- } else {
273
- this . restoreBackup ( ) ;
274
- }
275
- } ;
276
-
277
200
private onCopyClick = ( ) : void => {
278
201
const successful = copyNode ( this . recoveryKeyNode . current ) ;
279
202
if ( successful ) {
@@ -340,16 +263,28 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
340
263
} ;
341
264
342
265
private bootstrapSecretStorage = async ( ) : Promise < void > => {
266
+ const cli = MatrixClientPeg . safeGet ( ) ;
267
+ const crypto = cli . getCrypto ( ) ! ;
268
+ const { forceReset } = this . props ;
269
+
270
+ let backupInfo ;
271
+ // First, unless we know we want to do a reset, we see if there is an existing key backup
272
+ if ( ! forceReset ) {
273
+ try {
274
+ this . setState ( { phase : Phase . Loading } ) ;
275
+ backupInfo = await cli . getKeyBackupVersion ( ) ;
276
+ } catch ( e ) {
277
+ logger . error ( "Error fetching backup data from server" , e ) ;
278
+ this . setState ( { phase : Phase . LoadError } ) ;
279
+ return ;
280
+ }
281
+ }
282
+
343
283
this . setState ( {
344
284
phase : Phase . Storing ,
345
285
error : undefined ,
346
286
} ) ;
347
287
348
- const cli = MatrixClientPeg . safeGet ( ) ;
349
- const crypto = cli . getCrypto ( ) ! ;
350
-
351
- const { forceReset } = this . props ;
352
-
353
288
try {
354
289
if ( forceReset ) {
355
290
logger . log ( "Forcing secret storage reset" ) ;
@@ -371,8 +306,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
371
306
} ) ;
372
307
await crypto . bootstrapSecretStorage ( {
373
308
createSecretStorageKey : async ( ) => this . recoveryKey ! ,
374
- keyBackupInfo : this . state . backupInfo ! ,
375
- setupNewKeyBackup : ! this . state . backupInfo ,
309
+ setupNewKeyBackup : ! backupInfo ,
376
310
} ) ;
377
311
}
378
312
await initialiseDehydration ( true ) ;
@@ -381,20 +315,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
381
315
phase : Phase . Stored ,
382
316
} ) ;
383
317
} catch ( e ) {
384
- if (
385
- this . state . canUploadKeysWithPasswordOnly &&
386
- e instanceof MatrixError &&
387
- e . httpStatus === 401 &&
388
- e . data . flows
389
- ) {
390
- this . setState ( {
391
- accountPassword : "" ,
392
- accountPasswordCorrect : false ,
393
- phase : Phase . Migrate ,
394
- } ) ;
395
- } else {
396
- this . setState ( { error : true } ) ;
397
- }
318
+ this . setState ( { error : true } ) ;
398
319
logger . error ( "Error bootstrapping secret storage" , e ) ;
399
320
}
400
321
} ;
@@ -403,27 +324,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
403
324
this . props . onFinished ( false ) ;
404
325
} ;
405
326
406
- private restoreBackup = async ( ) : Promise < void > => {
407
- const { finished } = Modal . createDialog (
408
- RestoreKeyBackupDialog ,
409
- {
410
- showSummary : false ,
411
- } ,
412
- undefined ,
413
- /* priority = */ false ,
414
- /* static = */ false ,
415
- ) ;
416
-
417
- await finished ;
418
- const backupTrustInfo = await this . fetchBackupInfo ( ) ;
419
- if ( backupTrustInfo ?. trusted && this . state . canUploadKeysWithPasswordOnly && this . state . accountPassword ) {
420
- this . bootstrapSecretStorage ( ) ;
421
- }
422
- } ;
423
-
424
327
private onLoadRetryClick = ( ) : void => {
425
- this . setState ( { phase : Phase . Loading } ) ;
426
- this . fetchBackupInfo ( ) ;
328
+ this . bootstrapSecretStorage ( ) ;
427
329
} ;
428
330
429
331
private onShowKeyContinueClick = ( ) : void => {
@@ -495,12 +397,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
495
397
} ) ;
496
398
} ;
497
399
498
- private onAccountPasswordChange = ( e : React . ChangeEvent < HTMLInputElement > ) : void => {
499
- this . setState ( {
500
- accountPassword : e . target . value ,
501
- } ) ;
502
- } ;
503
-
504
400
private renderOptionKey ( ) : JSX . Element {
505
401
return (
506
402
< StyledRadioButton
@@ -565,55 +461,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
565
461
) ;
566
462
}
567
463
568
- private renderPhaseMigrate ( ) : JSX . Element {
569
- let authPrompt ;
570
- let nextCaption = _t ( "action|next" ) ;
571
- if ( this . state . canUploadKeysWithPasswordOnly ) {
572
- authPrompt = (
573
- < div >
574
- < div > { _t ( "settings|key_backup|setup_secure_backup|requires_password_confirmation" ) } </ div >
575
- < div >
576
- < Field
577
- id = "mx_CreateSecretStorageDialog_password"
578
- type = "password"
579
- label = { _t ( "common|password" ) }
580
- value = { this . state . accountPassword }
581
- onChange = { this . onAccountPasswordChange }
582
- forceValidity = { this . state . accountPasswordCorrect === false ? false : undefined }
583
- autoFocus = { true }
584
- />
585
- </ div >
586
- </ div >
587
- ) ;
588
- } else if ( ! this . state . backupTrustInfo ?. trusted ) {
589
- authPrompt = (
590
- < div >
591
- < div > { _t ( "settings|key_backup|setup_secure_backup|requires_key_restore" ) } </ div >
592
- </ div >
593
- ) ;
594
- nextCaption = _t ( "action|restore" ) ;
595
- } else {
596
- authPrompt = < p > { _t ( "settings|key_backup|setup_secure_backup|requires_server_authentication" ) } </ p > ;
597
- }
598
-
599
- return (
600
- < form onSubmit = { this . onMigrateFormSubmit } >
601
- < p > { _t ( "settings|key_backup|setup_secure_backup|session_upgrade_description" ) } </ p >
602
- < div > { authPrompt } </ div >
603
- < DialogButtons
604
- primaryButton = { nextCaption }
605
- onPrimaryButtonClick = { this . onMigrateFormSubmit }
606
- hasCancel = { false }
607
- primaryDisabled = { ! ! this . state . canUploadKeysWithPasswordOnly && ! this . state . accountPassword }
608
- >
609
- < button type = "button" className = "danger" onClick = { this . onCancelClick } >
610
- { _t ( "action|skip" ) }
611
- </ button >
612
- </ DialogButtons >
613
- </ form >
614
- ) ;
615
- }
616
-
617
464
private renderPhasePassPhrase ( ) : JSX . Element {
618
465
return (
619
466
< form onSubmit = { this . onPassPhraseNextClick } >
@@ -829,8 +676,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
829
676
switch ( phase ) {
830
677
case Phase . ChooseKeyPassphrase :
831
678
return _t ( "encryption|set_up_toast_title" ) ;
832
- case Phase . Migrate :
833
- return _t ( "settings|key_backup|setup_secure_backup|title_upgrade_encryption" ) ;
834
679
case Phase . Passphrase :
835
680
return _t ( "settings|key_backup|setup_secure_backup|title_set_phrase" ) ;
836
681
case Phase . PassphraseConfirm :
@@ -889,9 +734,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
889
734
case Phase . ChooseKeyPassphrase :
890
735
content = this . renderPhaseChooseKeyPassphrase ( ) ;
891
736
break ;
892
- case Phase . Migrate :
893
- content = this . renderPhaseMigrate ( ) ;
894
- break ;
895
737
case Phase . Passphrase :
896
738
content = this . renderPhasePassPhrase ( ) ;
897
739
break ;
0 commit comments