Update QR code rendering to support VerificationRequests

Makes for building the QR code easier and more common.
This commit is contained in:
Travis Ralston 2020-01-30 16:55:43 +00:00
parent 8a09cfbfbd
commit 724cff6a2e
2 changed files with 89 additions and 22 deletions

View file

@ -19,6 +19,9 @@ import PropTypes from "prop-types";
import {replaceableComponent} from "../../../../utils/replaceableComponent"; import {replaceableComponent} from "../../../../utils/replaceableComponent";
import * as qs from "qs"; import * as qs from "qs";
import QRCode from "qrcode-react"; 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") @replaceableComponent("views.elements.crypto.VerificationQRCode")
export default class VerificationQRCode extends React.PureComponent { export default class VerificationQRCode extends React.PureComponent {
@ -31,13 +34,81 @@ export default class VerificationQRCode extends React.PureComponent {
// User verification use case only // User verification use case only
secret: PropTypes.string, secret: PropTypes.string,
otherUserKey: PropTypes.string, // Base64 key being verified otherUserKey: PropTypes.string, // Base64 key being verified
requestEventId: PropTypes.string, otherUserDeviceKey: PropTypes.string, // Base64 key of the other user's device (optional)
requestEventId: PropTypes.string, // for DM verification only
}; };
static defaultProps = { static defaultProps = {
action: "verify", 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.estimatedTargetDevice;
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) {
// New -> Existing session QR code
qrProps.otherUserDeviceKey = null;
} else {
// Existing -> New session 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() { render() {
const query = { const query = {
request: this.props.requestEventId, request: this.props.requestEventId,

View file

@ -20,7 +20,6 @@ import PropTypes from "prop-types";
import * as sdk from '../../../index'; import * as sdk from '../../../index';
import {verificationMethods} from 'matrix-js-sdk/src/crypto'; import {verificationMethods} from 'matrix-js-sdk/src/crypto';
import VerificationQRCode from "../elements/crypto/VerificationQRCode"; import VerificationQRCode from "../elements/crypto/VerificationQRCode";
import {MatrixClientPeg} from "../../../MatrixClientPeg";
import {_t} from "../../../languageHandler"; import {_t} from "../../../languageHandler";
import E2EIcon from "../rooms/E2EIcon"; import E2EIcon from "../rooms/E2EIcon";
import { import {
@ -29,7 +28,7 @@ import {
PHASE_READY, PHASE_READY,
PHASE_DONE, PHASE_DONE,
PHASE_STARTED, PHASE_STARTED,
PHASE_CANCELLED, PHASE_CANCELLED, VerificationRequest,
} from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest";
import Spinner from "../elements/Spinner"; import Spinner from "../elements/Spinner";
@ -50,12 +49,24 @@ export default class VerificationPanel extends React.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
this.state = {}; this.state = {
qrCodeProps: null, // generated by the VerificationQRCode component itself
};
this._hasVerifier = false; 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) { renderQRPhase(pending) {
const {member, request} = this.props; const {member} = this.props;
const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); const AccessibleButton = sdk.getComponent('elements.AccessibleButton');
let button; let button;
@ -69,10 +80,7 @@ export default class VerificationPanel extends React.PureComponent {
); );
} }
const cli = MatrixClientPeg.get(); if (!this.state.qrCodeProps) {
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
return <div className="mx_UserInfo_container"> return <div className="mx_UserInfo_container">
<h3>Verify by emoji</h3> <h3>Verify by emoji</h3>
<p>{_t("Verify by comparing unique emoji.")}</p> <p>{_t("Verify by comparing unique emoji.")}</p>
@ -81,12 +89,6 @@ export default class VerificationPanel extends React.PureComponent {
</div>; </div>;
} }
const myKeyId = cli.getCrossSigningId();
const qrCodeKeys = [
[cli.getDeviceId(), cli.getDeviceEd25519Key()],
[myKeyId, myKeyId],
];
// TODO: add way to open camera to scan a QR code // TODO: add way to open camera to scan a QR code
return <React.Fragment> return <React.Fragment>
<div className="mx_UserInfo_container"> <div className="mx_UserInfo_container">
@ -96,13 +98,7 @@ export default class VerificationPanel extends React.PureComponent {
})}</p> })}</p>
<div className="mx_VerificationPanel_qrCode"> <div className="mx_VerificationPanel_qrCode">
<VerificationQRCode <VerificationQRCode {...this.state.qrCodeProps} />
keyholderUserId={MatrixClientPeg.get().getUserId()}
requestEventId={request.requestEvent.getId()}
otherUserKey={crossSigningInfo.getId("master")}
secret={request.encodedSharedSecret}
keys={qrCodeKeys}
/>
</div> </div>
</div> </div>