diff --git a/res/css/_components.scss b/res/css/_components.scss index 27ec1088c3..54e7436886 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -202,10 +202,10 @@ @import "./views/settings/_E2eAdvancedPanel.scss"; @import "./views/settings/_EmailAddresses.scss"; @import "./views/settings/_IntegrationManager.scss"; -@import "./views/settings/_KeyBackupPanel.scss"; @import "./views/settings/_Notifications.scss"; @import "./views/settings/_PhoneNumbers.scss"; @import "./views/settings/_ProfileSettings.scss"; +@import "./views/settings/_SecureBackupPanel.scss"; @import "./views/settings/_SetIdServer.scss"; @import "./views/settings/_SetIntegrationManager.scss"; @import "./views/settings/_UpdateCheckButton.scss"; diff --git a/res/css/views/settings/_KeyBackupPanel.scss b/res/css/views/settings/_SecureBackupPanel.scss similarity index 52% rename from res/css/views/settings/_KeyBackupPanel.scss rename to res/css/views/settings/_SecureBackupPanel.scss index 872162caad..a9dab06b57 100644 --- a/res/css/views/settings/_KeyBackupPanel.scss +++ b/res/css/views/settings/_SecureBackupPanel.scss @@ -1,6 +1,6 @@ /* Copyright 2018 New Vector Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2019, 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. @@ -15,23 +15,39 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_KeyBackupPanel_sigValid, .mx_KeyBackupPanel_sigInvalid, -.mx_KeyBackupPanel_deviceVerified, .mx_KeyBackupPanel_deviceNotVerified { +.mx_SecureBackupPanel_sigValid, .mx_SecureBackupPanel_sigInvalid, +.mx_SecureBackupPanel_deviceVerified, .mx_SecureBackupPanel_deviceNotVerified { font-weight: bold; } -.mx_KeyBackupPanel_sigValid, .mx_KeyBackupPanel_deviceVerified { +.mx_SecureBackupPanel_sigValid, .mx_SecureBackupPanel_deviceVerified { color: $e2e-verified-color; } -.mx_KeyBackupPanel_sigInvalid, .mx_KeyBackupPanel_deviceNotVerified { +.mx_SecureBackupPanel_sigInvalid, .mx_SecureBackupPanel_deviceNotVerified { color: $e2e-warning-color; } -.mx_KeyBackupPanel_deviceName { +.mx_SecureBackupPanel_deviceName { font-style: italic; } -.mx_KeyBackupPanel_buttonRow { +.mx_SecureBackupPanel_buttonRow { margin: 1em 0; + + :nth-child(n + 1) { + margin-inline-end: 10px; + } +} + +.mx_SecureBackupPanel_statusList { + border-spacing: 0; + + td { + padding: 0; + + &:first-of-type { + padding-inline-end: 1em; + } + } } diff --git a/res/css/views/settings/tabs/_SettingsTab.scss b/res/css/views/settings/tabs/_SettingsTab.scss index 5f00ed86f7..892f5fe744 100644 --- a/res/css/views/settings/tabs/_SettingsTab.scss +++ b/res/css/views/settings/tabs/_SettingsTab.scss @@ -14,6 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ +.mx_SettingsTab { + color: $muted-fg-color; +} + .mx_SettingsTab_warningText { color: $warning-color; } diff --git a/src/SecurityManager.js b/src/SecurityManager.js index 891f43b705..cc7db3ead7 100644 --- a/src/SecurityManager.js +++ b/src/SecurityManager.js @@ -250,7 +250,7 @@ export async function accessSecretStorage(func = async () => { }, forceReset = f 'Cross-signing keys dialog', '', InteractiveAuthDialog, { title: _t("Setting up keys"), - matrixClient: MatrixClientPeg.get(), + matrixClient: cli, makeRequest, }, ); diff --git a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js index 07ff3c9b76..d4b1a73c3e 100644 --- a/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js +++ b/src/async-components/views/dialogs/secretstorage/CreateSecretStorageDialog.js @@ -280,6 +280,9 @@ export default class CreateSecretStorageDialog extends React.PureComponent { const { forceReset } = this.props; try { + // JRS: In an upcoming change, the cross-signing steps will be + // removed from here and this will instead be about secret storage + // only. if (forceReset) { console.log("Forcing cross-signing and secret storage reset"); await cli.bootstrapSecretStorage({ diff --git a/src/components/views/settings/CrossSigningPanel.js b/src/components/views/settings/CrossSigningPanel.js index 3eeb072e2d..a0ca84645f 100644 --- a/src/components/views/settings/CrossSigningPanel.js +++ b/src/components/views/settings/CrossSigningPanel.js @@ -19,9 +19,9 @@ import React from 'react'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; -import { accessSecretStorage } from '../../../SecurityManager'; import Modal from '../../../Modal'; import Spinner from '../elements/Spinner'; +import InteractiveAuthDialog from '../dialogs/InteractiveAuthDialog'; export default class CrossSigningPanel extends React.PureComponent { constructor(props) { @@ -31,13 +31,13 @@ export default class CrossSigningPanel extends React.PureComponent { this.state = { error: null, - crossSigningPublicKeysOnDevice: false, - crossSigningPrivateKeysInStorage: false, - masterPrivateKeyCached: false, - selfSigningPrivateKeyCached: false, - userSigningPrivateKeyCached: false, - sessionBackupKeyCached: false, - secretStorageKeyInAccount: false, + crossSigningPublicKeysOnDevice: null, + crossSigningPrivateKeysInStorage: null, + masterPrivateKeyCached: null, + selfSigningPrivateKeyCached: null, + userSigningPrivateKeyCached: null, + homeserverSupportsCrossSigning: null, + crossSigningReady: null, }; } @@ -66,7 +66,7 @@ export default class CrossSigningPanel extends React.PureComponent { }; _onBootstrapClick = () => { - this._bootstrapSecureSecretStorage(false); + this._bootstrapCrossSigning({ forceReset: false }); }; onStatusChanged = () => { @@ -83,14 +83,9 @@ export default class CrossSigningPanel extends React.PureComponent { const masterPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("master")); const selfSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("self_signing")); const userSigningPrivateKeyCached = !!(pkCache && await pkCache.getCrossSigningKeyCache("user_signing")); - const sessionBackupKeyFromCache = await cli._crypto.getSessionBackupPrivateKey(); - const sessionBackupKeyCached = !!(sessionBackupKeyFromCache); - const sessionBackupKeyWellFormed = sessionBackupKeyFromCache instanceof Uint8Array; - const secretStorageKeyInAccount = await secretStorage.hasKey(); const homeserverSupportsCrossSigning = await cli.doesServerSupportUnstableFeature("org.matrix.e2e_cross_signing"); const crossSigningReady = await cli.isCrossSigningReady(); - const secretStorageReady = await cli.isSecretStorageReady(); this.setState({ crossSigningPublicKeysOnDevice, @@ -98,45 +93,56 @@ export default class CrossSigningPanel extends React.PureComponent { masterPrivateKeyCached, selfSigningPrivateKeyCached, userSigningPrivateKeyCached, - sessionBackupKeyCached, - sessionBackupKeyWellFormed, - secretStorageKeyInAccount, homeserverSupportsCrossSigning, crossSigningReady, - secretStorageReady, }); } /** - * Bootstrapping secret storage may take one of these paths: - * 1. Create secret storage from a passphrase and store cross-signing keys - * in secret storage. + * Bootstrapping cross-signing take one of these paths: + * 1. Create cross-signing keys locally and store in secret storage (if it + * already exists on the account). * 2. Access existing secret storage by requesting passphrase and accessing * cross-signing keys as needed. * 3. All keys are loaded and there's nothing to do. * @param {bool} [forceReset] Bootstrap again even if keys already present */ - _bootstrapSecureSecretStorage = async (forceReset=false) => { + _bootstrapCrossSigning = async ({ forceReset = false }) => { this.setState({ error: null }); try { - await accessSecretStorage(() => undefined, forceReset); + const cli = MatrixClientPeg.get(); + await cli.bootstrapCrossSigning({ + authUploadDeviceSigningKeys: async (makeRequest) => { + const { finished } = Modal.createTrackedDialog( + 'Cross-signing keys dialog', '', InteractiveAuthDialog, + { + title: _t("Setting up keys"), + matrixClient: cli, + makeRequest, + }, + ); + const [confirmed] = await finished; + if (!confirmed) { + throw new Error("Cross-signing key upload auth canceled"); + } + }, + setupNewCrossSigning: forceReset, + }); } catch (e) { this.setState({ error: e }); - console.error("Error bootstrapping secret storage", e); + console.error("Error bootstrapping cross-signing", e); } if (this._unmounted) return; this._getUpdatedStatus(); } - onDestroyStorage = (act) => { - if (!act) return; - this._bootstrapSecureSecretStorage(true); - } - - _destroySecureSecretStorage = () => { + _resetCrossSigning = () => { const ConfirmDestroyCrossSigningDialog = sdk.getComponent("dialogs.ConfirmDestroyCrossSigningDialog"); Modal.createDialog(ConfirmDestroyCrossSigningDialog, { - onFinished: this.onDestroyStorage, + onFinished: (act) => { + if (!act) return; + this._bootstrapCrossSigning({ forceReset: true }); + }, }); } @@ -149,12 +155,8 @@ export default class CrossSigningPanel extends React.PureComponent { masterPrivateKeyCached, selfSigningPrivateKeyCached, userSigningPrivateKeyCached, - sessionBackupKeyCached, - sessionBackupKeyWellFormed, - secretStorageKeyInAccount, homeserverSupportsCrossSigning, crossSigningReady, - secretStorageReady, } = this.state; let errorSection; @@ -169,14 +171,9 @@ export default class CrossSigningPanel extends React.PureComponent { summarisedStatus =
{_t( "Your homeserver does not support cross-signing.", )}
; - } else if (crossSigningReady && secretStorageReady) { + } else if (crossSigningReady) { summarisedStatus =✅ {_t( - "Cross-signing and secret storage are ready for use.", - )}
; - } else if (crossSigningReady && !secretStorageReady) { - summarisedStatus =✅ {_t( - "Cross-signing is ready for use, but secret storage is " + - "currently not being used to backup your keys.", + "Cross-signing is ready for use.", )}
; } else if (crossSigningPrivateKeysInStorage) { summarisedStatus ={_t( @@ -185,17 +182,15 @@ export default class CrossSigningPanel extends React.PureComponent { )}
; } else { summarisedStatus ={_t( - "Cross-signing and secret storage are not yet set up.", + "Cross-signing is not set up.", )}
; } const keysExistAnywhere = ( - secretStorageKeyInAccount || crossSigningPrivateKeysInStorage || crossSigningPublicKeysOnDevice ); const keysExistEverywhere = ( - secretStorageKeyInAccount && crossSigningPrivateKeysInStorage && crossSigningPublicKeysOnDevice ); @@ -204,8 +199,8 @@ export default class CrossSigningPanel extends React.PureComponent { if (keysExistAnywhere) { resetButton = ({encryptedMessageAreEncrypted}
-✅ {_t("This session is backing up your keys. ")}
-✅ {_t("This session is backing up your keys. ")}
; } else { - clientBackupStatus ={encryptedMessageAreEncrypted}
+ statusDescription = <>{_t( "This session is not backing up your keys, " + "but you do have an existing backup you can restore from " + @@ -200,19 +241,11 @@ export default class KeyBackupPanel extends React.PureComponent { "Connect this session to key backup before signing out to avoid " + "losing any keys that may only be on this session.", )}
-{_t( - "Your keys are not being backed up from this session.", {}, - {b: sub => {sub}}, - )}
-{encryptedMessageAreEncrypted}
-{_t("Back up your keys before signing out to avoid losing them.")}
-{_t( + "Your keys are not being backed up from this session.", {}, + {b: sub => {sub}}, + )}
+{_t("Back up your keys before signing out to avoid losing them.")}
+ >; + actions.push( +{_t( + "Back up your encryption keys with your account data in case you " + + "lose access to your sessions. Your keys will be secured with a " + + "unique Recovery Key.", + )}
+ {statusDescription} +{_t("Backup key stored:")} | +{ + backupKeyStored === true ? _t("in secret storage") : _t("not stored") + } | +
{_t("Backup key cached:")} | ++ {backupKeyCached ? _t("cached locally") : _t("not found locally")} + {backupKeyWellFormedText} + | +
{_t("Secret storage public key:")} | +{secretStorageKeyInAccount ? _t("in account data") : _t("not found")} | +
{_t("Secret storage:")} | +{secretStorageReady ? _t("ready") : _t("not ready")} | +