@@ -11,7 +11,7 @@ import React, { createRef } from "react";
1111import FileSaver from "file-saver" ;
1212import { logger } from "matrix-js-sdk/src/logger" ;
1313import { 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" ;
1515import classNames from "classnames" ;
1616import CheckmarkIcon from "@vector-im/compound-design-tokens/assets/web/icons/check" ;
1717
@@ -25,7 +25,6 @@ import StyledRadioButton from "../../../../components/views/elements/StyledRadio
2525import AccessibleButton from "../../../../components/views/elements/AccessibleButton" ;
2626import DialogButtons from "../../../../components/views/elements/DialogButtons" ;
2727import InlineSpinner from "../../../../components/views/elements/InlineSpinner" ;
28- import RestoreKeyBackupDialog from "../../../../components/views/dialogs/security/RestoreKeyBackupDialog" ;
2928import {
3029 getSecureBackupSetupMethods ,
3130 isSecureBackupRequired ,
@@ -45,7 +44,6 @@ enum Phase {
4544 Loading = "loading" ,
4645 LoadError = "load_error" ,
4746 ChooseKeyPassphrase = "choose_key_passphrase" ,
48- Migrate = "migrate" ,
4947 Passphrase = "passphrase" ,
5048 PassphraseConfirm = "passphrase_confirm" ,
5149 ShowKey = "show_key" ,
@@ -72,24 +70,6 @@ interface IState {
7270 downloaded : boolean ;
7371 setPassphrase : boolean ;
7472
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-
9373 // does the server offer a UI auth flow with just m.login.password
9474 // for /keys/device_signing/upload?
9575 canUploadKeysWithPasswordOnly : boolean | null ;
@@ -141,16 +121,17 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
141121 this . queryKeyUploadAuth ( ) ;
142122 }
143123
124+ const keyFromCustomisations = ModuleRunner . instance . extensions . cryptoSetup . createSecretStorageKey ( ) ;
125+ const phase = keyFromCustomisations ? Phase . Loading : Phase . ChooseKeyPassphrase ;
126+
144127 this . state = {
145- phase : Phase . Loading ,
128+ phase,
146129 passPhrase : "" ,
147130 passPhraseValid : false ,
148131 passPhraseConfirm : "" ,
149132 copied : false ,
150133 downloaded : false ,
151134 setPassphrase : false ,
152- backupInfo : null ,
153- backupTrustInfo : undefined ,
154135 // does the server offer a UI auth flow with just m.login.password
155136 // for /keys/device_signing/upload?
156137 accountPasswordCorrect : null ,
@@ -160,60 +141,15 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
160141 accountPassword,
161142 } ;
162143
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 ) ;
184145 }
185146
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 ( ) ;
217153 }
218154
219155 private async queryKeyUploadAuth ( ) : Promise < void > {
@@ -237,10 +173,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
237173 }
238174 }
239175
240- private onKeyBackupStatusChange = ( ) : void => {
241- if ( this . state . phase === Phase . Migrate ) this . fetchBackupInfo ( ) ;
242- } ;
243-
244176 private onKeyPassphraseChange = ( e : React . ChangeEvent < HTMLInputElement > ) : void => {
245177 this . setState ( {
246178 passPhraseKeySelected : e . target . value ,
@@ -265,15 +197,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
265197 }
266198 } ;
267199
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-
277200 private onCopyClick = ( ) : void => {
278201 const successful = copyNode ( this . recoveryKeyNode . current ) ;
279202 if ( successful ) {
@@ -340,16 +263,28 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
340263 } ;
341264
342265 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+
343283 this . setState ( {
344284 phase : Phase . Storing ,
345285 error : undefined ,
346286 } ) ;
347287
348- const cli = MatrixClientPeg . safeGet ( ) ;
349- const crypto = cli . getCrypto ( ) ! ;
350-
351- const { forceReset } = this . props ;
352-
353288 try {
354289 if ( forceReset ) {
355290 logger . log ( "Forcing secret storage reset" ) ;
@@ -371,8 +306,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
371306 } ) ;
372307 await crypto . bootstrapSecretStorage ( {
373308 createSecretStorageKey : async ( ) => this . recoveryKey ! ,
374- keyBackupInfo : this . state . backupInfo ! ,
375- setupNewKeyBackup : ! this . state . backupInfo ,
309+ setupNewKeyBackup : ! backupInfo ,
376310 } ) ;
377311 }
378312 await initialiseDehydration ( true ) ;
@@ -381,20 +315,7 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
381315 phase : Phase . Stored ,
382316 } ) ;
383317 } 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 } ) ;
398319 logger . error ( "Error bootstrapping secret storage" , e ) ;
399320 }
400321 } ;
@@ -403,27 +324,8 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
403324 this . props . onFinished ( false ) ;
404325 } ;
405326
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-
424327 private onLoadRetryClick = ( ) : void => {
425- this . setState ( { phase : Phase . Loading } ) ;
426- this . fetchBackupInfo ( ) ;
328+ this . bootstrapSecretStorage ( ) ;
427329 } ;
428330
429331 private onShowKeyContinueClick = ( ) : void => {
@@ -495,12 +397,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
495397 } ) ;
496398 } ;
497399
498- private onAccountPasswordChange = ( e : React . ChangeEvent < HTMLInputElement > ) : void => {
499- this . setState ( {
500- accountPassword : e . target . value ,
501- } ) ;
502- } ;
503-
504400 private renderOptionKey ( ) : JSX . Element {
505401 return (
506402 < StyledRadioButton
@@ -565,55 +461,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
565461 ) ;
566462 }
567463
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-
617464 private renderPhasePassPhrase ( ) : JSX . Element {
618465 return (
619466 < form onSubmit = { this . onPassPhraseNextClick } >
@@ -829,8 +676,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
829676 switch ( phase ) {
830677 case Phase . ChooseKeyPassphrase :
831678 return _t ( "encryption|set_up_toast_title" ) ;
832- case Phase . Migrate :
833- return _t ( "settings|key_backup|setup_secure_backup|title_upgrade_encryption" ) ;
834679 case Phase . Passphrase :
835680 return _t ( "settings|key_backup|setup_secure_backup|title_set_phrase" ) ;
836681 case Phase . PassphraseConfirm :
@@ -889,9 +734,6 @@ export default class CreateSecretStorageDialog extends React.PureComponent<IProp
889734 case Phase . ChooseKeyPassphrase :
890735 content = this . renderPhaseChooseKeyPassphrase ( ) ;
891736 break ;
892- case Phase . Migrate :
893- content = this . renderPhaseMigrate ( ) ;
894- break ;
895737 case Phase . Passphrase :
896738 content = this . renderPhasePassPhrase ( ) ;
897739 break ;
0 commit comments