From 5866d67c883f1c1ce7398a92fbd5482feb0b7f9f Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Mon, 10 Feb 2020 16:18:44 +0100 Subject: [PATCH] Alterations to DeviceVerifyDialog to support picking QR code --- .../views/dialogs/DeviceVerifyDialog.js | 68 ++++++++++++--- .../VerificationQREmojiOptions.js | 84 +++++++++++++++++++ src/i18n/strings/en_EN.json | 7 +- 3 files changed, 146 insertions(+), 13 deletions(-) create mode 100644 src/components/views/verification/VerificationQREmojiOptions.js diff --git a/src/components/views/dialogs/DeviceVerifyDialog.js b/src/components/views/dialogs/DeviceVerifyDialog.js index d78ef3ca83..0b2470c92b 100644 --- a/src/components/views/dialogs/DeviceVerifyDialog.js +++ b/src/components/views/dialogs/DeviceVerifyDialog.js @@ -27,16 +27,19 @@ import {verificationMethods} from 'matrix-js-sdk/src/crypto'; import {ensureDMExists} from "../../../createRoom"; import dis from "../../../dispatcher"; import SettingsStore from '../../../settings/SettingsStore'; +import {SHOW_QR_CODE_METHOD} from "matrix-js-sdk/src/crypto/verification/QRCode"; +import VerificationQREmojiOptions from "../verification/VerificationQREmojiOptions"; const MODE_LEGACY = 'legacy'; const MODE_SAS = 'sas'; const PHASE_START = 0; const PHASE_WAIT_FOR_PARTNER_TO_ACCEPT = 1; -const PHASE_SHOW_SAS = 2; -const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 3; -const PHASE_VERIFIED = 4; -const PHASE_CANCELLED = 5; +const PHASE_PICK_VERIFICATION_OPTION = 2; +const PHASE_SHOW_SAS = 3; +const PHASE_WAIT_FOR_PARTNER_TO_CONFIRM = 4; +const PHASE_VERIFIED = 5; +const PHASE_CANCELLED = 6; export default class DeviceVerifyDialog extends React.Component { static propTypes = { @@ -49,6 +52,7 @@ export default class DeviceVerifyDialog extends React.Component { super(); this._verifier = null; this._showSasEvent = null; + this._request = null; this.state = { phase: PHASE_START, mode: MODE_SAS, @@ -80,6 +84,25 @@ export default class DeviceVerifyDialog extends React.Component { this.props.onFinished(false); } + _onUseSasClick = async () => { + try { + this._verifier = this._request.beginKeyVerification(verificationMethods.SAS); + this._verifier.on('show_sas', this._onVerifierShowSas); + // throws upon cancellation + await this._verifier.verify(); + this.setState({phase: PHASE_VERIFIED}); + this._verifier.removeListener('show_sas', this._onVerifierShowSas); + this._verifier = null; + } catch (e) { + console.log("Verification failed", e); + this.setState({ + phase: PHASE_CANCELLED, + }); + this._verifier = null; + this._request = null; + } + }; + _onLegacyFinished = (confirm) => { if (confirm) { MatrixClientPeg.get().setDeviceVerified( @@ -108,11 +131,20 @@ export default class DeviceVerifyDialog extends React.Component { } else { this._verifier = request.verifier; } + } else if (verifyingOwnDevice && SettingsStore.isFeatureEnabled("feature_cross_signing")) { + this._request = await client.requestVerification(this.props.userId,[ + verificationMethods.SAS, + SHOW_QR_CODE_METHOD, + ]); + + await this._request.waitFor(r => r.ready || r.started); + this.setState({phase: PHASE_PICK_VERIFICATION_OPTION}); } else { this._verifier = client.beginKeyVerification( verificationMethods.SAS, this.props.userId, this.props.device.deviceId, ); } + if (!this._verifier) return; this._verifier.on('show_sas', this._onVerifierShowSas); // throws upon cancellation await this._verifier.verify(); @@ -150,10 +182,13 @@ export default class DeviceVerifyDialog extends React.Component { let body; switch (this.state.phase) { case PHASE_START: - body = this._renderSasVerificationPhaseStart(); + body = this._renderVerificationPhaseStart(); break; case PHASE_WAIT_FOR_PARTNER_TO_ACCEPT: - body = this._renderSasVerificationPhaseWaitAccept(); + body = this._renderVerificationPhaseWaitAccept(); + break; + case PHASE_PICK_VERIFICATION_OPTION: + body = this._renderVerificationPhasePick(); break; case PHASE_SHOW_SAS: body = this._renderSasVerificationPhaseShowSas(); @@ -162,10 +197,10 @@ export default class DeviceVerifyDialog extends React.Component { body = this._renderSasVerificationPhaseWaitForPartnerToConfirm(); break; case PHASE_VERIFIED: - body = this._renderSasVerificationPhaseVerified(); + body = this._renderVerificationPhaseVerified(); break; case PHASE_CANCELLED: - body = this._renderSasVerificationPhaseCancelled(); + body = this._renderVerificationPhaseCancelled(); break; } @@ -180,7 +215,7 @@ export default class DeviceVerifyDialog extends React.Component { ); } - _renderSasVerificationPhaseStart() { + _renderVerificationPhaseStart() { const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton'); const DialogButtons = sdk.getComponent('views.elements.DialogButtons'); return ( @@ -206,7 +241,7 @@ export default class DeviceVerifyDialog extends React.Component { ); } - _renderSasVerificationPhaseWaitAccept() { + _renderVerificationPhaseWaitAccept() { const Spinner = sdk.getComponent("views.elements.Spinner"); const AccessibleButton = sdk.getComponent('views.elements.AccessibleButton'); @@ -227,6 +262,14 @@ export default class DeviceVerifyDialog extends React.Component { ); } + _renderVerificationPhasePick() { + return ; + } + _renderSasVerificationPhaseShowSas() { const VerificationShowSas = sdk.getComponent('views.verification.VerificationShowSas'); return ; } @@ -247,12 +291,12 @@ export default class DeviceVerifyDialog extends React.Component { ; } - _renderSasVerificationPhaseVerified() { + _renderVerificationPhaseVerified() { const VerificationComplete = sdk.getComponent('views.verification.VerificationComplete'); return ; } - _renderSasVerificationPhaseCancelled() { + _renderVerificationPhaseCancelled() { const VerificationCancelled = sdk.getComponent('views.verification.VerificationCancelled'); return ; } diff --git a/src/components/views/verification/VerificationQREmojiOptions.js b/src/components/views/verification/VerificationQREmojiOptions.js new file mode 100644 index 0000000000..07f3b30f50 --- /dev/null +++ b/src/components/views/verification/VerificationQREmojiOptions.js @@ -0,0 +1,84 @@ +/* +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 React from 'react'; +import PropTypes from 'prop-types'; +import { _t, _td } from '../../../languageHandler'; +import {PendingActionSpinner} from "../right_panel/EncryptionInfo"; +import AccessibleButton from "../elements/AccessibleButton"; +import DialogButtons from "../elements/DialogButtons"; +import {replaceableComponent} from "../../../utils/replaceableComponent"; +import VerificationQRCode from "../elements/crypto/VerificationQRCode"; +import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import Spinner from "../elements/Spinner"; + +@replaceableComponent("views.verification.VerificationQREmojiOptions") +export default class VerificationQREmojiOptions extends React.Component { + static propTypes = { + request: PropTypes.object.isRequired, + onCancel: PropTypes.func.isRequired, + onStartEmoji: PropTypes.func.isRequired, + }; + + constructor(props) { + super(props); + + this.state = { + qrProps: null, + }; + + this._prepareQrCode(props.request); + } + + async _prepareQrCode(request: VerificationRequest) { + try { + const props = await VerificationQRCode.getPropsForRequest(request); + this.setState({qrProps: props}); + } catch (e) { + console.error(e); + // We just won't show a QR code + } + } + + render() { + let qrCode =
; + if (this.state.qrProps) { + qrCode = ; + } + return ( +
+ {_t("Verify this session by completing one of the following:")} +
+
+

{_t("Scan this unique code")}

+ {qrCode} +
+ {_t("or")} +
+

{_t("Compare unique emoji")}

+ {_t("Compare a unique set of emoji if you don't have a camera on either device")} + + {_t("Start")} + +
+
+ + {_t("Cancel")} + +
+ ); + } +} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 83c15fc385..3bff66f258 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -451,6 +451,12 @@ "You've successfully verified this user.": "You've successfully verified this user.", "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.": "Secure messages with this user are end-to-end encrypted and not able to be read by third parties.", "Got It": "Got It", + "Verify this session by completing one of the following:": "Verify this session by completing one of the following:", + "Scan this unique code": "Scan this unique code", + "or": "or", + "Compare unique emoji": "Compare unique emoji", + "Compare a unique set of emoji if you don't have a camera on either device": "Compare a unique set of emoji if you don't have a camera on either device", + "Start": "Start", "Confirm the emoji below are displayed on both devices, in the same order:": "Confirm the emoji below are displayed on both devices, in the same order:", "Verify this user by confirming the following emoji appear on their screen.": "Verify this user by confirming the following emoji appear on their screen.", "Verify this device by confirming the following number appears on its screen.": "Verify this device by confirming the following number appears on its screen.", @@ -1924,7 +1930,6 @@ "Could not load user profile": "Could not load user profile", "Complete security": "Complete security", "Verify this session to grant it access to encrypted messages.": "Verify this session to grant it access to encrypted messages.", - "Start": "Start", "Session verified": "Session verified", "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.",