diff --git a/src/components/views/elements/crypto/VerificationQRCode.js b/src/components/views/elements/crypto/VerificationQRCode.js index 630a06a07c..1c0fdcbf44 100644 --- a/src/components/views/elements/crypto/VerificationQRCode.js +++ b/src/components/views/elements/crypto/VerificationQRCode.js @@ -19,6 +19,9 @@ import PropTypes from "prop-types"; import {replaceableComponent} from "../../../../utils/replaceableComponent"; import * as qs from "qs"; import QRCode from "qrcode-react"; +import {MatrixClientPeg} from "../../../../MatrixClientPeg"; +import {VerificationRequest} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import {ToDeviceChannel} from "matrix-js-sdk/src/crypto/verification/request/ToDeviceChannel"; @replaceableComponent("views.elements.crypto.VerificationQRCode") export default class VerificationQRCode extends React.PureComponent { @@ -31,13 +34,81 @@ export default class VerificationQRCode extends React.PureComponent { // User verification use case only secret: PropTypes.string, otherUserKey: PropTypes.string, // Base64 key being verified - requestEventId: PropTypes.string, + otherUserDeviceKey: PropTypes.string, // Base64 key of the other user's device (or what we think it is; optional) + requestEventId: PropTypes.string, // for DM verification only }; static defaultProps = { action: "verify", }; + static async getPropsForRequest(verificationRequest: VerificationRequest) { + const cli = MatrixClientPeg.get(); + const myUserId = cli.getUserId(); + const otherUserId = verificationRequest.otherUserId; + const myDeviceId = cli.getDeviceId(); + const otherDevice = verificationRequest.targetDevice; + const otherDeviceId = otherDevice ? otherDevice.deviceId : null; + + const qrProps = { + secret: verificationRequest.encodedSharedSecret, + keyholderUserId: myUserId, + action: "verify", + keys: [], // array of pairs: keyId, base64Key + otherUserKey: "", // base64key + otherUserDeviceKey: "", // base64key + requestEventId: "", // we figure this out in a moment + }; + + const requestEvent = verificationRequest.requestEvent; + qrProps.requestEventId = requestEvent.getId() + ? requestEvent.getId() + : ToDeviceChannel.getTransactionId(requestEvent); + + // Populate the keys we need depending on which direction and users are involved in the verification. + if (myUserId === otherUserId) { + if (!otherDeviceId) { + // Existing scanning New session's QR code + qrProps.otherUserDeviceKey = null; + } else { + // New scanning Existing session's QR code + const myDevices = (await cli.getStoredDevicesForUser(myUserId)) || []; + const device = myDevices.find(d => d.deviceId === otherDeviceId); + if (device) qrProps.otherUserDeviceKey = device.getFingerprint(); + } + + // Either direction shares these next few props + + const xsignInfo = cli.getStoredCrossSigningForUser(myUserId); + qrProps.otherUserKey = xsignInfo.getId("master"); + + qrProps.keys = [ + [myDeviceId, cli.getDeviceEd25519Key()], + [xsignInfo.getId("master"), xsignInfo.getId("master")], + ]; + } else { + // Doesn't matter which direction the verification is, we always show the same QR code + // for not-ourself verification. + const myXsignInfo = cli.getStoredCrossSigningForUser(myUserId); + const otherXsignInfo = cli.getStoredCrossSigningForUser(otherUserId); + const otherDevices = (await cli.getStoredDevicesForUser(otherUserId)) || []; + const otherDevice = otherDevices.find(d => d.deviceId === otherDeviceId); + + qrProps.keys = [ + [myDeviceId, cli.getDeviceEd25519Key()], + [myXsignInfo.getId("master"), myXsignInfo.getId("master")], + ]; + qrProps.otherUserKey = otherXsignInfo.getId("master"); + if (otherDevice) qrProps.otherUserDeviceKey = otherDevice.getFingerprint(); + } + + return qrProps; + } + + constructor(props) { + super(props); + } + render() { const query = { request: this.props.requestEventId, diff --git a/src/components/views/right_panel/VerificationPanel.js b/src/components/views/right_panel/VerificationPanel.js index 3527747a66..ad1aaf598c 100644 --- a/src/components/views/right_panel/VerificationPanel.js +++ b/src/components/views/right_panel/VerificationPanel.js @@ -20,7 +20,6 @@ import PropTypes from "prop-types"; import * as sdk from '../../../index'; import {verificationMethods} from 'matrix-js-sdk/src/crypto'; import VerificationQRCode from "../elements/crypto/VerificationQRCode"; -import {MatrixClientPeg} from "../../../MatrixClientPeg"; import {_t} from "../../../languageHandler"; import E2EIcon from "../rooms/E2EIcon"; import { @@ -29,7 +28,7 @@ import { PHASE_READY, PHASE_DONE, PHASE_STARTED, - PHASE_CANCELLED, + PHASE_CANCELLED, VerificationRequest, } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; import Spinner from "../elements/Spinner"; @@ -50,12 +49,24 @@ export default class VerificationPanel extends React.PureComponent { constructor(props) { super(props); - this.state = {}; + this.state = { + qrCodeProps: null, // generated by the VerificationQRCode component itself + }; this._hasVerifier = false; + this._generateQRCodeProps(props.request); + } + + async _generateQRCodeProps(verificationRequest: VerificationRequest) { + try { + this.setState({qrCodeProps: await VerificationQRCode.getPropsForRequest(verificationRequest)}); + } catch (e) { + console.error(e); + // Do nothing - we won't render a QR code. + } } renderQRPhase(pending) { - const {member, request} = this.props; + const {member} = this.props; const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); let button; @@ -69,10 +80,7 @@ export default class VerificationPanel extends React.PureComponent { ); } - const cli = MatrixClientPeg.get(); - const crossSigningInfo = cli.getStoredCrossSigningForUser(request.otherUserId); - if (!crossSigningInfo || !request.requestEvent || !request.requestEvent.getId()) { - // for whatever reason we can't generate a QR code, offer only SAS Verification + if (!this.state.qrCodeProps) { return
{_t("Verify by comparing unique emoji.")}
@@ -81,12 +89,6 @@ export default class VerificationPanel extends React.PureComponent {