Support accounts with cross signing but no SSSS
Port https://github.com/matrix-org/matrix-react-sdk/pull/4717 to release
This commit is contained in:
parent
5256a86545
commit
518db90b69
5 changed files with 79 additions and 41 deletions
|
@ -1870,42 +1870,35 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
this.accountPasswordTimer = null;
|
this.accountPasswordTimer = null;
|
||||||
}, 60 * 5 * 1000);
|
}, 60 * 5 * 1000);
|
||||||
|
|
||||||
// Wait for the client to be logged in (but not started)
|
// Create and start the client
|
||||||
// which is enough to ask the server about account data.
|
await Lifecycle.setLoggedIn(credentials);
|
||||||
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;
|
|
||||||
|
|
||||||
const cli = MatrixClientPeg.get();
|
const cli = MatrixClientPeg.get();
|
||||||
// We're checking `isCryptoAvailable` here instead of `isCryptoEnabled`
|
const cryptoEnabled = cli.isCryptoEnabled();
|
||||||
// because the client hasn't been started yet.
|
if (!cryptoEnabled) {
|
||||||
const cryptoAvailable = isCryptoAvailable();
|
|
||||||
if (!cryptoAvailable) {
|
|
||||||
this.onLoggedIn();
|
this.onLoggedIn();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({ pendingInitialSync: true });
|
const promisesList = [this.firstSyncPromise.promise];
|
||||||
await this.firstSyncPromise.promise;
|
if (cryptoEnabled) {
|
||||||
|
// wait for the client to finish downloading cross-signing keys for us so we
|
||||||
if (!cryptoAvailable) {
|
// know whether or not we have keys set up on this account
|
||||||
this.setState({ pendingInitialSync: false });
|
promisesList.push(cli.downloadKeys([cli.getUserId()]));
|
||||||
return setLoggedInPromise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test for the master cross-signing key in SSSS as a quick proxy for
|
// Now update the state to say we're waiting for the first sync to complete rather
|
||||||
// whether cross-signing has been set up on the account.
|
// than for the login to finish.
|
||||||
const masterKeyInStorage = !!cli.getAccountData("m.cross_signing.master");
|
this.setState({ pendingInitialSync: true });
|
||||||
if (masterKeyInStorage) {
|
|
||||||
|
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 });
|
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
|
||||||
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
|
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
|
||||||
this.setStateForNewView({ view: Views.E2E_SETUP });
|
this.setStateForNewView({ view: Views.E2E_SETUP });
|
||||||
|
@ -1913,8 +1906,6 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
|
||||||
this.onLoggedIn();
|
this.onLoggedIn();
|
||||||
}
|
}
|
||||||
this.setState({ pendingInitialSync: false });
|
this.setState({ pendingInitialSync: false });
|
||||||
|
|
||||||
return setLoggedInPromise;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// complete security / e2e setup has finished
|
// complete security / e2e setup has finished
|
||||||
|
|
|
@ -378,7 +378,7 @@ export default createReactClass({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.access_token) {
|
if (response.access_token) {
|
||||||
const cli = await this.props.onLoggedIn({
|
await this.props.onLoggedIn({
|
||||||
userId: response.user_id,
|
userId: response.user_id,
|
||||||
deviceId: response.device_id,
|
deviceId: response.device_id,
|
||||||
homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
|
homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
|
||||||
|
@ -386,7 +386,7 @@ export default createReactClass({
|
||||||
accessToken: response.access_token,
|
accessToken: response.access_token,
|
||||||
}, this.state.formVals.password);
|
}, this.state.formVals.password);
|
||||||
|
|
||||||
this._setupPushers(cli);
|
this._setupPushers();
|
||||||
// we're still busy until we get unmounted: don't show the registration form again
|
// we're still busy until we get unmounted: don't show the registration form again
|
||||||
newState.busy = true;
|
newState.busy = true;
|
||||||
} else {
|
} else {
|
||||||
|
@ -397,10 +397,11 @@ export default createReactClass({
|
||||||
this.setState(newState);
|
this.setState(newState);
|
||||||
},
|
},
|
||||||
|
|
||||||
_setupPushers: function(matrixClient) {
|
_setupPushers: function() {
|
||||||
if (!this.props.brand) {
|
if (!this.props.brand) {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
|
const matrixClient = MatrixClientPeg.get();
|
||||||
return matrixClient.getPushers().then((resp)=>{
|
return matrixClient.getPushers().then((resp)=>{
|
||||||
const pushers = resp.pushers;
|
const pushers = resp.pushers;
|
||||||
for (let i = 0; i < pushers.length; ++i) {
|
for (let i = 0; i < pushers.length; ++i) {
|
||||||
|
|
|
@ -28,6 +28,14 @@ import {
|
||||||
PHASE_FINISHED,
|
PHASE_FINISHED,
|
||||||
} from '../../../stores/SetupEncryptionStore';
|
} from '../../../stores/SetupEncryptionStore';
|
||||||
|
|
||||||
|
function keyHasPassphrase(keyInfo) {
|
||||||
|
return (
|
||||||
|
keyInfo.passphrase &&
|
||||||
|
keyInfo.passphrase.salt &&
|
||||||
|
keyInfo.passphrase.iterations
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default class SetupEncryptionBody extends React.Component {
|
export default class SetupEncryptionBody extends React.Component {
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
onFinished: PropTypes.func.isRequired,
|
onFinished: PropTypes.func.isRequired,
|
||||||
|
@ -108,6 +116,21 @@ export default class SetupEncryptionBody extends React.Component {
|
||||||
member={MatrixClientPeg.get().getUser(this.state.verificationRequest.otherUserId)}
|
member={MatrixClientPeg.get().getUser(this.state.verificationRequest.otherUserId)}
|
||||||
/>;
|
/>;
|
||||||
} else if (phase === PHASE_INTRO) {
|
} 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 (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<p>{_t(
|
<p>{_t(
|
||||||
|
@ -131,9 +154,7 @@ export default class SetupEncryptionBody extends React.Component {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="mx_CompleteSecurity_actionRow">
|
<div className="mx_CompleteSecurity_actionRow">
|
||||||
<AccessibleButton kind="link" onClick={this._onUsePassphraseClick}>
|
{useRecoveryKeyButton}
|
||||||
{_t("Use Recovery Passphrase or Key")}
|
|
||||||
</AccessibleButton>
|
|
||||||
<AccessibleButton kind="danger" onClick={this.onSkipClick}>
|
<AccessibleButton kind="danger" onClick={this.onSkipClick}>
|
||||||
{_t("Skip")}
|
{_t("Skip")}
|
||||||
</AccessibleButton>
|
</AccessibleButton>
|
||||||
|
|
|
@ -2121,10 +2121,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.",
|
"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",
|
"Registration Successful": "Registration Successful",
|
||||||
"Create your account": "Create your account",
|
"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.",
|
"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:",
|
"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",
|
"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. 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.",
|
"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.",
|
"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,11 +36,20 @@ export class SetupEncryptionStore extends EventEmitter {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this._started = true;
|
this._started = true;
|
||||||
this.phase = PHASE_INTRO;
|
this.phase = PHASE_BUSY;
|
||||||
this.verificationRequest = null;
|
this.verificationRequest = null;
|
||||||
this.backupInfo = 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);
|
||||||
|
|
||||||
|
this.fetchKeyInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
stop() {
|
stop() {
|
||||||
|
@ -57,6 +66,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() {
|
async usePassPhrase() {
|
||||||
this.phase = PHASE_BUSY;
|
this.phase = PHASE_BUSY;
|
||||||
this.emit("update");
|
this.emit("update");
|
||||||
|
|
Loading…
Reference in a new issue