Skip to content

Commit 7c9b8ba

Browse files
authored
Merge pull request matrix-org#4852 from matrix-org/jryans/no-4s-release
Support accounts with cross signing but no SSSS
2 parents 5256a86 + 518db90 commit 7c9b8ba

File tree

5 files changed

+76
-38
lines changed

5 files changed

+76
-38
lines changed

src/components/structures/MatrixChat.tsx

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1870,51 +1870,42 @@ export default class MatrixChat extends React.PureComponent<IProps, IState> {
18701870
this.accountPasswordTimer = null;
18711871
}, 60 * 5 * 1000);
18721872

1873-
// Wait for the client to be logged in (but not started)
1874-
// which is enough to ask the server about account data.
1875-
const loggedIn = new Promise(resolve => {
1876-
const actionHandlerRef = dis.register(payload => {
1877-
if (payload.action !== "on_logged_in") {
1878-
return;
1879-
}
1880-
dis.unregister(actionHandlerRef);
1881-
resolve();
1882-
});
1883-
});
1884-
1885-
// Create and start the client in the background
1886-
const setLoggedInPromise = Lifecycle.setLoggedIn(credentials);
1887-
await loggedIn;
1873+
// Create and start the client
1874+
await Lifecycle.setLoggedIn(credentials);
18881875

18891876
const cli = MatrixClientPeg.get();
1890-
// We're checking `isCryptoAvailable` here instead of `isCryptoEnabled`
1891-
// because the client hasn't been started yet.
1892-
const cryptoAvailable = isCryptoAvailable();
1893-
if (!cryptoAvailable) {
1877+
const cryptoEnabled = cli.isCryptoEnabled();
1878+
if (!cryptoEnabled) {
18941879
this.onLoggedIn();
18951880
}
18961881

1882+
const promisesList = [this.firstSyncPromise.promise];
1883+
if (cryptoEnabled) {
1884+
// wait for the client to finish downloading cross-signing keys for us so we
1885+
// know whether or not we have keys set up on this account
1886+
promisesList.push(cli.downloadKeys([cli.getUserId()]));
1887+
}
1888+
1889+
// Now update the state to say we're waiting for the first sync to complete rather
1890+
// than for the login to finish.
18971891
this.setState({ pendingInitialSync: true });
1898-
await this.firstSyncPromise.promise;
18991892

1900-
if (!cryptoAvailable) {
1893+
await Promise.all(promisesList);
1894+
1895+
if (!cryptoEnabled) {
19011896
this.setState({ pendingInitialSync: false });
1902-
return setLoggedInPromise;
1897+
return;
19031898
}
19041899

1905-
// Test for the master cross-signing key in SSSS as a quick proxy for
1906-
// whether cross-signing has been set up on the account.
1907-
const masterKeyInStorage = !!cli.getAccountData("m.cross_signing.master");
1908-
if (masterKeyInStorage) {
1900+
const crossSigningIsSetUp = cli.getStoredCrossSigningForUser(cli.getUserId());
1901+
if (crossSigningIsSetUp) {
19091902
this.setStateForNewView({ view: Views.COMPLETE_SECURITY });
19101903
} else if (await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")) {
19111904
this.setStateForNewView({ view: Views.E2E_SETUP });
19121905
} else {
19131906
this.onLoggedIn();
19141907
}
19151908
this.setState({ pendingInitialSync: false });
1916-
1917-
return setLoggedInPromise;
19181909
};
19191910

19201911
// complete security / e2e setup has finished

src/components/structures/auth/Registration.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -378,15 +378,15 @@ export default createReactClass({
378378
}
379379

380380
if (response.access_token) {
381-
const cli = await this.props.onLoggedIn({
381+
await this.props.onLoggedIn({
382382
userId: response.user_id,
383383
deviceId: response.device_id,
384384
homeserverUrl: this.state.matrixClient.getHomeserverUrl(),
385385
identityServerUrl: this.state.matrixClient.getIdentityServerUrl(),
386386
accessToken: response.access_token,
387387
}, this.state.formVals.password);
388388

389-
this._setupPushers(cli);
389+
this._setupPushers();
390390
// we're still busy until we get unmounted: don't show the registration form again
391391
newState.busy = true;
392392
} else {
@@ -397,10 +397,11 @@ export default createReactClass({
397397
this.setState(newState);
398398
},
399399

400-
_setupPushers: function(matrixClient) {
400+
_setupPushers: function() {
401401
if (!this.props.brand) {
402402
return Promise.resolve();
403403
}
404+
const matrixClient = MatrixClientPeg.get();
404405
return matrixClient.getPushers().then((resp)=>{
405406
const pushers = resp.pushers;
406407
for (let i = 0; i < pushers.length; ++i) {

src/components/structures/auth/SetupEncryptionBody.js

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,14 @@ import {
2828
PHASE_FINISHED,
2929
} from '../../../stores/SetupEncryptionStore';
3030

31+
function keyHasPassphrase(keyInfo) {
32+
return (
33+
keyInfo.passphrase &&
34+
keyInfo.passphrase.salt &&
35+
keyInfo.passphrase.iterations
36+
);
37+
}
38+
3139
export default class SetupEncryptionBody extends React.Component {
3240
static propTypes = {
3341
onFinished: PropTypes.func.isRequired,
@@ -108,6 +116,21 @@ export default class SetupEncryptionBody extends React.Component {
108116
member={MatrixClientPeg.get().getUser(this.state.verificationRequest.otherUserId)}
109117
/>;
110118
} else if (phase === PHASE_INTRO) {
119+
const store = SetupEncryptionStore.sharedInstance();
120+
let recoveryKeyPrompt;
121+
if (store.keyInfo && keyHasPassphrase(store.keyInfo)) {
122+
recoveryKeyPrompt = _t("Use Recovery Key or Passphrase");
123+
} else if (store.keyInfo) {
124+
recoveryKeyPrompt = _t("Use Recovery Key");
125+
}
126+
127+
let useRecoveryKeyButton;
128+
if (recoveryKeyPrompt) {
129+
useRecoveryKeyButton = <AccessibleButton kind="link" onClick={this._onUsePassphraseClick}>
130+
{recoveryKeyPrompt}
131+
</AccessibleButton>;
132+
}
133+
111134
return (
112135
<div>
113136
<p>{_t(
@@ -131,9 +154,7 @@ export default class SetupEncryptionBody extends React.Component {
131154
</div>
132155

133156
<div className="mx_CompleteSecurity_actionRow">
134-
<AccessibleButton kind="link" onClick={this._onUsePassphraseClick}>
135-
{_t("Use Recovery Passphrase or Key")}
136-
</AccessibleButton>
157+
{useRecoveryKeyButton}
137158
<AccessibleButton kind="danger" onClick={this.onSkipClick}>
138159
{_t("Skip")}
139160
</AccessibleButton>

src/i18n/strings/en_EN.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2121,10 +2121,11 @@
21212121
"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.",
21222122
"Registration Successful": "Registration Successful",
21232123
"Create your account": "Create your account",
2124+
"Use Recovery Key or Passphrase": "Use Recovery Key or Passphrase",
2125+
"Use Recovery Key": "Use Recovery Key",
21242126
"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.",
21252127
"This requires the latest Riot on your other devices:": "This requires the latest Riot on your other devices:",
21262128
"or another cross-signing capable Matrix client": "or another cross-signing capable Matrix client",
2127-
"Use Recovery Passphrase or Key": "Use Recovery Passphrase or Key",
21282129
"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.",
21292130
"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.",
21302131
"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.",

src/stores/SetupEncryptionStore.js

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,20 @@ export class SetupEncryptionStore extends EventEmitter {
3636
return;
3737
}
3838
this._started = true;
39-
this.phase = PHASE_INTRO;
39+
this.phase = PHASE_BUSY;
4040
this.verificationRequest = null;
4141
this.backupInfo = null;
42-
MatrixClientPeg.get().on("crypto.verification.request", this.onVerificationRequest);
43-
MatrixClientPeg.get().on('userTrustStatusChanged', this._onUserTrustStatusChanged);
42+
43+
// ID of the key that the secrets we want are encrypted with
44+
this.keyId = null;
45+
// Descriptor of the key that the secrets we want are encrypted with
46+
this.keyInfo = null;
47+
48+
const cli = MatrixClientPeg.get();
49+
cli.on("crypto.verification.request", this.onVerificationRequest);
50+
cli.on('userTrustStatusChanged', this._onUserTrustStatusChanged);
51+
52+
this.fetchKeyInfo();
4453
}
4554

4655
stop() {
@@ -57,6 +66,21 @@ export class SetupEncryptionStore extends EventEmitter {
5766
}
5867
}
5968

69+
async fetchKeyInfo() {
70+
const keys = await MatrixClientPeg.get().isSecretStored('m.cross_signing.master', false);
71+
if (keys === null || Object.keys(keys).length === 0) {
72+
this.keyId = null;
73+
this.keyInfo = null;
74+
} else {
75+
// If the secret is stored under more than one key, we just pick an arbitrary one
76+
this.keyId = Object.keys(keys)[0];
77+
this.keyInfo = keys[this.keyId];
78+
}
79+
80+
this.phase = PHASE_INTRO;
81+
this.emit("update");
82+
}
83+
6084
async usePassPhrase() {
6185
this.phase = PHASE_BUSY;
6286
this.emit("update");

0 commit comments

Comments
 (0)