Merge pull request #4717 from matrix-org/dbkr/support_no_ssss
Support accounts with cross signing but no SSSSpull/21833/head
						commit
						48bb86f0bf
					
				|  | @ -1870,42 +1870,35 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { | |||
|             this.accountPasswordTimer = null; | ||||
|         }, 60 * 5 * 1000); | ||||
| 
 | ||||
|         // Wait for the client to be logged in (but not started)
 | ||||
|         // which is enough to ask the server about account data.
 | ||||
|         const loggedIn = new Promise(resolve => { | ||||
|             const actionHandlerRef = dis.register(payload => { | ||||
|                 if (payload.action !== "on_logged_in") { | ||||
|                     return; | ||||
|                 } | ||||
|                 dis.unregister(actionHandlerRef); | ||||
|                 resolve(); | ||||
|             }); | ||||
|         }); | ||||
| 
 | ||||
|         // Create and start the client in the background
 | ||||
|         const setLoggedInPromise = Lifecycle.setLoggedIn(credentials); | ||||
|         await loggedIn; | ||||
|         // Create and start the client
 | ||||
|         await Lifecycle.setLoggedIn(credentials); | ||||
| 
 | ||||
|         const cli = MatrixClientPeg.get(); | ||||
|         // We're checking `isCryptoAvailable` here instead of `isCryptoEnabled`
 | ||||
|         // because the client hasn't been started yet.
 | ||||
|         const cryptoAvailable = isCryptoAvailable(); | ||||
|         if (!cryptoAvailable) { | ||||
|         const cryptoEnabled = cli.isCryptoEnabled(); | ||||
|         if (!cryptoEnabled) { | ||||
|             this.onLoggedIn(); | ||||
|         } | ||||
| 
 | ||||
|         this.setState({ pendingInitialSync: true }); | ||||
|         await this.firstSyncPromise.promise; | ||||
| 
 | ||||
|         if (!cryptoAvailable) { | ||||
|             this.setState({ pendingInitialSync: false }); | ||||
|             return setLoggedInPromise; | ||||
|         const promisesList = [this.firstSyncPromise.promise]; | ||||
|         if (cryptoEnabled) { | ||||
|             // wait for the client to finish downloading cross-signing keys for us so we
 | ||||
|             // know whether or not we have keys set up on this account
 | ||||
|             promisesList.push(cli.downloadKeys([cli.getUserId()])); | ||||
|         } | ||||
| 
 | ||||
|         // Test for the master cross-signing key in SSSS as a quick proxy for
 | ||||
|         // whether cross-signing has been set up on the account.
 | ||||
|         const masterKeyInStorage = !!cli.getAccountData("m.cross_signing.master"); | ||||
|         if (masterKeyInStorage) { | ||||
|         // Now update the state to say we're waiting for the first sync to complete rather
 | ||||
|         // than for the login to finish.
 | ||||
|         this.setState({ pendingInitialSync: true }); | ||||
| 
 | ||||
|         await Promise.all(promisesList); | ||||
| 
 | ||||
|         if (!cryptoEnabled) { | ||||
|             this.setState({ pendingInitialSync: false }); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId()); | ||||
|         if (crossSigningIsSetUp) { | ||||
|             this.setStateForNewView({ view: Views.COMPLETE_SECURITY }); | ||||
|         } else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) { | ||||
|             this.setStateForNewView({ view: Views.E2E_SETUP }); | ||||
|  | @ -1913,8 +1906,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> { | |||
|             this.onLoggedIn(); | ||||
|         } | ||||
|         this.setState({ pendingInitialSync: false }); | ||||
| 
 | ||||
|         return setLoggedInPromise; | ||||
|     }; | ||||
| 
 | ||||
|     // complete security / e2e setup has finished
 | ||||
|  |  | |||
|  | @ -378,7 +378,7 @@ export default createReactClass({ | |||
|         } | ||||
| 
 | ||||
|         if (response.access_token) { | ||||
|             const cli = await this.props.onLoggedIn({ | ||||
|             await this.props.onLoggedIn({ | ||||
|                 userId: response.user_id, | ||||
|                 deviceId: response.device_id, | ||||
|                 homeserverUrl: this.state.matrixClient.getHomeserverUrl(), | ||||
|  | @ -386,7 +386,7 @@ export default createReactClass({ | |||
|                 accessToken: response.access_token, | ||||
|             }, this.state.formVals.password); | ||||
| 
 | ||||
|             this._setupPushers(cli); | ||||
|             this._setupPushers(); | ||||
|             // we're still busy until we get unmounted: don't show the registration form again
 | ||||
|             newState.busy = true; | ||||
|         } else { | ||||
|  | @ -397,10 +397,11 @@ export default createReactClass({ | |||
|         this.setState(newState); | ||||
|     }, | ||||
| 
 | ||||
|     _setupPushers: function(matrixClient) { | ||||
|     _setupPushers: function() { | ||||
|         if (!this.props.brand) { | ||||
|             return Promise.resolve(); | ||||
|         } | ||||
|         const matrixClient = MatrixClientPeg.get(); | ||||
|         return matrixClient.getPushers().then((resp)=>{ | ||||
|             const pushers = resp.pushers; | ||||
|             for (let i = 0; i < pushers.length; ++i) { | ||||
|  |  | |||
|  | @ -28,6 +28,14 @@ import { | |||
|     PHASE_FINISHED, | ||||
| } from '../../../stores/SetupEncryptionStore'; | ||||
| 
 | ||||
| function keyHasPassphrase(keyInfo) { | ||||
|     return ( | ||||
|         keyInfo.passphrase && | ||||
|         keyInfo.passphrase.salt && | ||||
|         keyInfo.passphrase.iterations | ||||
|     ); | ||||
| } | ||||
| 
 | ||||
| export default class SetupEncryptionBody extends React.Component { | ||||
|     static propTypes = { | ||||
|         onFinished: PropTypes.func.isRequired, | ||||
|  | @ -108,6 +116,21 @@ export default class SetupEncryptionBody extends React.Component { | |||
|                 member={MatrixClientPeg.get().getUser(this.state.verificationRequest.otherUserId)} | ||||
|             />; | ||||
|         } else if (phase === PHASE_INTRO) { | ||||
|             const store = SetupEncryptionStore.sharedInstance(); | ||||
|             let recoveryKeyPrompt; | ||||
|             if (store.keyInfo && keyHasPassphrase(store.keyInfo)) { | ||||
|                 recoveryKeyPrompt = _t("Use Recovery Key or Passphrase"); | ||||
|             } else if (store.keyInfo) { | ||||
|                 recoveryKeyPrompt = _t("Use Recovery Key"); | ||||
|             } | ||||
| 
 | ||||
|             let useRecoveryKeyButton; | ||||
|             if (recoveryKeyPrompt) { | ||||
|                 useRecoveryKeyButton = <AccessibleButton kind="link" onClick={this._onUsePassphraseClick}> | ||||
|                     {recoveryKeyPrompt} | ||||
|                 </AccessibleButton>; | ||||
|             } | ||||
| 
 | ||||
|             return ( | ||||
|                 <div> | ||||
|                     <p>{_t( | ||||
|  | @ -131,9 +154,7 @@ export default class SetupEncryptionBody extends React.Component { | |||
|                     </div> | ||||
| 
 | ||||
|                     <div className="mx_CompleteSecurity_actionRow"> | ||||
|                         <AccessibleButton kind="link" onClick={this._onUsePassphraseClick}> | ||||
|                             {_t("Use Recovery Passphrase or Key")} | ||||
|                         </AccessibleButton> | ||||
|                         {useRecoveryKeyButton} | ||||
|                         <AccessibleButton kind="danger" onClick={this.onSkipClick}> | ||||
|                             {_t("Skip")} | ||||
|                         </AccessibleButton> | ||||
|  |  | |||
|  | @ -2119,10 +2119,11 @@ | |||
|     "You can now close this window or <a>log in</a> to your new account.": "You can now close this window or <a>log in</a> to your new account.", | ||||
|     "Registration Successful": "Registration Successful", | ||||
|     "Create your account": "Create your account", | ||||
|     "Use Recovery Key or Passphrase": "Use Recovery Key or Passphrase", | ||||
|     "Use Recovery Key": "Use Recovery Key", | ||||
|     "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.": "Confirm your identity by verifying this login from one of your other sessions, granting it access to encrypted messages.", | ||||
|     "This requires the latest Riot on your other devices:": "This requires the latest Riot on your other devices:", | ||||
|     "or another cross-signing capable Matrix client": "or another cross-signing capable Matrix client", | ||||
|     "Use Recovery Passphrase or Key": "Use Recovery Passphrase or Key", | ||||
|     "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.": "Your new session is now verified. It has access to your encrypted messages, and other users will see it as trusted.", | ||||
|     "Your new session is now verified. Other users will see it as trusted.": "Your new session is now verified. Other users will see it as trusted.", | ||||
|     "Without completing security on this session, it won’t have access to encrypted messages.": "Without completing security on this session, it won’t have access to encrypted messages.", | ||||
|  |  | |||
|  | @ -36,13 +36,19 @@ export class SetupEncryptionStore extends EventEmitter { | |||
|             return; | ||||
|         } | ||||
|         this._started = true; | ||||
|         this.phase = PHASE_INTRO; | ||||
|         this.phase = PHASE_BUSY; | ||||
|         this.verificationRequest = null; | ||||
|         this.backupInfo = null; | ||||
|         MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequest); | ||||
|         MatrixClientPeg.get().on('userTrustStatusChanged', this._onUserTrustStatusChanged); | ||||
| 
 | ||||
|         // ID of the key that the secrets we want are encrypted with
 | ||||
|         this.keyId = null; | ||||
|         // Descriptor of the key that the secrets we want are encrypted with
 | ||||
|         this.keyInfo = null; | ||||
| 
 | ||||
|         const cli = MatrixClientPeg.get(); | ||||
|         cli.on("crypto.verification.request", this.onVerificationRequest); | ||||
|         cli.on('userTrustStatusChanged', this._onUserTrustStatusChanged); | ||||
| 
 | ||||
|         const requestsInProgress = cli.getVerificationRequestsToDeviceInProgress(cli.getUserId()); | ||||
|         if (requestsInProgress.length) { | ||||
|             // If there are multiple, we take the most recent. Equally if the user sends another request from
 | ||||
|  | @ -68,6 +74,21 @@ export class SetupEncryptionStore extends EventEmitter { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     async fetchKeyInfo() { | ||||
|         const keys = await MatrixClientPeg.get().isSecretStored('m.cross_signing.master', false); | ||||
|         if (keys === null || Object.keys(keys).length === 0) { | ||||
|             this.keyId = null; | ||||
|             this.keyInfo = null; | ||||
|         } else { | ||||
|             // If the secret is stored under more than one key, we just pick an arbitrary one
 | ||||
|             this.keyId = Object.keys(keys)[0]; | ||||
|             this.keyInfo = keys[this.keyId]; | ||||
|         } | ||||
| 
 | ||||
|         this.phase = PHASE_INTRO; | ||||
|         this.emit("update"); | ||||
|     } | ||||
| 
 | ||||
|     async usePassPhrase() { | ||||
|         this.phase = PHASE_BUSY; | ||||
|         this.emit("update"); | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 David Baker
						David Baker