Merge pull request #4118 from matrix-org/jryans/hs-xsign-check

Check for cross-signing homeserver support
This commit is contained in:
J. Ryan Stinnett 2020-02-24 16:58:57 +00:00 committed by GitHub
commit 64dc776e43
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 92 additions and 42 deletions

View file

@ -53,7 +53,6 @@ function selectText(target) {
*/ */
export default class CreateKeyBackupDialog extends React.PureComponent { export default class CreateKeyBackupDialog extends React.PureComponent {
static propTypes = { static propTypes = {
secureSecretStorage: PropTypes.bool,
onFinished: PropTypes.func.isRequired, onFinished: PropTypes.func.isRequired,
} }
@ -65,7 +64,7 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
this._setZxcvbnResultTimeout = null; this._setZxcvbnResultTimeout = null;
this.state = { this.state = {
secureSecretStorage: props.secureSecretStorage, secureSecretStorage: null,
phase: PHASE_PASSPHRASE, phase: PHASE_PASSPHRASE,
passPhrase: '', passPhrase: '',
passPhraseConfirm: '', passPhraseConfirm: '',
@ -73,23 +72,20 @@ export default class CreateKeyBackupDialog extends React.PureComponent {
downloaded: false, downloaded: false,
zxcvbnResult: null, zxcvbnResult: null,
}; };
if (this.state.secureSecretStorage === undefined) {
this.state.secureSecretStorage =
SettingsStore.isFeatureEnabled("feature_cross_signing");
} }
async componentDidMount() {
const cli = MatrixClientPeg.get();
const secureSecretStorage = (
SettingsStore.isFeatureEnabled("feature_cross_signing") &&
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing")
);
this.setState({ secureSecretStorage });
// If we're using secret storage, skip ahead to the backing up step, as // If we're using secret storage, skip ahead to the backing up step, as
// `accessSecretStorage` will handle passphrases as needed. // `accessSecretStorage` will handle passphrases as needed.
if (this.state.secureSecretStorage) { if (secureSecretStorage) {
this.state.phase = PHASE_BACKINGUP; this.setState({ phase: PHASE_BACKINGUP });
}
}
componentDidMount() {
// If we're using secret storage, skip ahead to the backing up step, as
// `accessSecretStorage` will handle passphrases as needed.
if (this.state.secureSecretStorage) {
this._createBackup(); this._createBackup();
} }
} }

View file

@ -42,6 +42,7 @@ import {textualPowerLevel} from '../../../Roles';
import MatrixClientContext from "../../../contexts/MatrixClientContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext";
import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases"; import {RIGHT_PANEL_PHASES} from "../../../stores/RightPanelStorePhases";
import EncryptionPanel from "./EncryptionPanel"; import EncryptionPanel from "./EncryptionPanel";
import { useAsyncMemo } from '../../../hooks/useAsyncMemo';
const _disambiguateDevices = (devices) => { const _disambiguateDevices = (devices) => {
const names = Object.create(null); const names = Object.create(null);
@ -916,6 +917,12 @@ const useIsSynapseAdmin = (cli) => {
return isAdmin; return isAdmin;
}; };
const useHomeserverSupportsCrossSigning = (cli) => {
return useAsyncMemo(async () => {
return cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
}, [cli], false);
};
function useRoomPermissions(cli, room, user) { function useRoomPermissions(cli, room, user) {
const [roomPermissions, setRoomPermissions] = useState({ const [roomPermissions, setRoomPermissions] = useState({
// modifyLevelMax is the max PL we can set this user to, typically min(their PL, our PL) && canSetPL // modifyLevelMax is the max PL we can set this user to, typically min(their PL, our PL) && canSetPL
@ -1315,13 +1322,16 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => {
text = _t("Messages in this room are end-to-end encrypted."); text = _t("Messages in this room are end-to-end encrypted.");
} }
let verifyButton;
const homeserverSupportsCrossSigning = useHomeserverSupportsCrossSigning(cli);
if (
SettingsStore.isFeatureEnabled("feature_cross_signing") &&
homeserverSupportsCrossSigning
) {
const userTrust = cli.checkUserTrust(member.userId); const userTrust = cli.checkUserTrust(member.userId);
const userVerified = SettingsStore.isFeatureEnabled("feature_cross_signing") ? const userVerified = userTrust.isCrossSigningVerified();
userTrust.isCrossSigningVerified() :
userTrust.isVerified();
const isMe = member.userId === cli.getUserId(); const isMe = member.userId === cli.getUserId();
let verifyButton;
if (isRoomEncrypted && !userVerified && !isMe) { if (isRoomEncrypted && !userVerified && !isMe) {
verifyButton = ( verifyButton = (
<AccessibleButton className="mx_UserInfo_field" onClick={() => verifyUser(member)}> <AccessibleButton className="mx_UserInfo_field" onClick={() => verifyUser(member)}>
@ -1329,6 +1339,7 @@ const BasicUserInfo = ({room, member, groupId, devices, isRoomEncrypted}) => {
</AccessibleButton> </AccessibleButton>
); );
} }
}
let devicesSection; let devicesSection;
if (isRoomEncrypted) { if (isRoomEncrypted) {

View file

@ -72,11 +72,14 @@ export default class CrossSigningPanel extends React.PureComponent {
const crossSigningPublicKeysOnDevice = crossSigning.getId(); const crossSigningPublicKeysOnDevice = crossSigning.getId();
const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage); const crossSigningPrivateKeysInStorage = await crossSigning.isStoredInSecretStorage(secretStorage);
const secretStorageKeyInAccount = await secretStorage.hasKey(); const secretStorageKeyInAccount = await secretStorage.hasKey();
const homeserverSupportsCrossSigning =
await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing");
this.setState({ this.setState({
crossSigningPublicKeysOnDevice, crossSigningPublicKeysOnDevice,
crossSigningPrivateKeysInStorage, crossSigningPrivateKeysInStorage,
secretStorageKeyInAccount, secretStorageKeyInAccount,
homeserverSupportsCrossSigning,
}); });
} }
@ -120,6 +123,7 @@ export default class CrossSigningPanel extends React.PureComponent {
crossSigningPublicKeysOnDevice, crossSigningPublicKeysOnDevice,
crossSigningPrivateKeysInStorage, crossSigningPrivateKeysInStorage,
secretStorageKeyInAccount, secretStorageKeyInAccount,
homeserverSupportsCrossSigning,
} = this.state; } = this.state;
let errorSection; let errorSection;
@ -127,13 +131,19 @@ export default class CrossSigningPanel extends React.PureComponent {
errorSection = <div className="error">{error.toString()}</div>; errorSection = <div className="error">{error.toString()}</div>;
} }
const enabled = ( // Whether the various keys exist on your account (but not necessarily
// on this device).
const enabledForAccount = (
crossSigningPrivateKeysInStorage && crossSigningPrivateKeysInStorage &&
secretStorageKeyInAccount secretStorageKeyInAccount
); );
let summarisedStatus; let summarisedStatus;
if (enabled && crossSigningPublicKeysOnDevice) { if (!homeserverSupportsCrossSigning) {
summarisedStatus = <p>{_t(
"Your homeserver does not support cross-signing.",
)}</p>;
} else if (enabledForAccount && crossSigningPublicKeysOnDevice) {
summarisedStatus = <p> {_t( summarisedStatus = <p> {_t(
"Cross-signing and secret storage are enabled.", "Cross-signing and secret storage are enabled.",
)}</p>; )}</p>;
@ -149,18 +159,18 @@ export default class CrossSigningPanel extends React.PureComponent {
} }
let bootstrapButton; let bootstrapButton;
if (!enabled) { if (enabledForAccount) {
bootstrapButton = <div className="mx_CrossSigningPanel_buttonRow"> bootstrapButton = (
<AccessibleButton kind="primary" onClick={this._bootstrapSecureSecretStorage}>
{_t("Bootstrap cross-signing and secret storage")}
</AccessibleButton>
</div>;
} else {
bootstrapButton = <div className="mx_CrossSigningPanel_buttonRow">
<AccessibleButton kind="danger" onClick={this._destroySecureSecretStorage}> <AccessibleButton kind="danger" onClick={this._destroySecureSecretStorage}>
{_t("Reset cross-signing and secret storage")} {_t("Reset cross-signing and secret storage")}
</AccessibleButton> </AccessibleButton>
</div>; );
} else if (!enabledForAccount && homeserverSupportsCrossSigning) {
bootstrapButton = (
<AccessibleButton kind="primary" onClick={this._bootstrapSecureSecretStorage}>
{_t("Bootstrap cross-signing and secret storage")}
</AccessibleButton>
);
} }
return ( return (
@ -181,11 +191,17 @@ export default class CrossSigningPanel extends React.PureComponent {
<td>{_t("Secret storage public key:")}</td> <td>{_t("Secret storage public key:")}</td>
<td>{secretStorageKeyInAccount ? _t("in account data") : _t("not found")}</td> <td>{secretStorageKeyInAccount ? _t("in account data") : _t("not found")}</td>
</tr> </tr>
<tr>
<td>{_t("Homeserver feature support:")}</td>
<td>{homeserverSupportsCrossSigning ? _t("exists") : _t("not found")}</td>
</tr>
</tbody></table> </tbody></table>
</details> </details>
{errorSection} {errorSection}
<div className="mx_CrossSigningPanel_buttonRow">
{bootstrapButton} {bootstrapButton}
</div> </div>
</div>
); );
} }
} }

View file

@ -127,7 +127,6 @@ export default class KeyBackupPanel extends React.PureComponent {
Modal.createTrackedDialogAsync('Key Backup', 'Key Backup', Modal.createTrackedDialogAsync('Key Backup', 'Key Backup',
import('../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'), import('../../../async-components/views/dialogs/keybackup/CreateKeyBackupDialog'),
{ {
secureSecretStorage: SettingsStore.isFeatureEnabled("feature_cross_signing"),
onFinished: () => { onFinished: () => {
this._loadBackupStatus(); this._loadBackupStatus();
}, },

25
src/hooks/useAsyncMemo.js Normal file
View file

@ -0,0 +1,25 @@
/*
Copyright 2020 The Matrix.org Foundation C.I.C.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import { useState, useEffect } from 'react';
export const useAsyncMemo = (fn, deps, initialValue) => {
const [value, setValue] = useState(initialValue);
useEffect(() => {
fn().then(setValue);
}, deps); // eslint-disable-line react-hooks/exhaustive-deps
return value;
};

View file

@ -562,11 +562,12 @@
"New Password": "New Password", "New Password": "New Password",
"Confirm password": "Confirm password", "Confirm password": "Confirm password",
"Change Password": "Change Password", "Change Password": "Change Password",
"Your homeserver does not support cross-signing.": "Your homeserver does not support cross-signing.",
"Cross-signing and secret storage are enabled.": "Cross-signing and secret storage are enabled.", "Cross-signing and secret storage are enabled.": "Cross-signing and secret storage are enabled.",
"Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.", "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.": "Your account has a cross-signing identity in secret storage, but it is not yet trusted by this session.",
"Cross-signing and secret storage are not yet set up.": "Cross-signing and secret storage are not yet set up.", "Cross-signing and secret storage are not yet set up.": "Cross-signing and secret storage are not yet set up.",
"Bootstrap cross-signing and secret storage": "Bootstrap cross-signing and secret storage",
"Reset cross-signing and secret storage": "Reset cross-signing and secret storage", "Reset cross-signing and secret storage": "Reset cross-signing and secret storage",
"Bootstrap cross-signing and secret storage": "Bootstrap cross-signing and secret storage",
"Cross-signing public keys:": "Cross-signing public keys:", "Cross-signing public keys:": "Cross-signing public keys:",
"in memory": "in memory", "in memory": "in memory",
"not found": "not found", "not found": "not found",
@ -574,6 +575,8 @@
"in secret storage": "in secret storage", "in secret storage": "in secret storage",
"Secret storage public key:": "Secret storage public key:", "Secret storage public key:": "Secret storage public key:",
"in account data": "in account data", "in account data": "in account data",
"Homeserver feature support:": "Homeserver feature support:",
"exists": "exists",
"Your homeserver does not support session management.": "Your homeserver does not support session management.", "Your homeserver does not support session management.": "Your homeserver does not support session management.",
"Unable to load session list": "Unable to load session list", "Unable to load session list": "Unable to load session list",
"Authentication": "Authentication", "Authentication": "Authentication",