Merge pull request #1233 from matrix-org/rav/async_crypto

Prepare for asynchronous e2e APIs
This commit is contained in:
Richard van der Hoff 2017-07-19 21:11:36 +01:00 committed by GitHub
commit 0e8ad75248
5 changed files with 93 additions and 62 deletions

View file

@ -78,6 +78,18 @@ class MatrixClientPeg {
} }
async start() { async start() {
// try to initialise e2e on the new client
try {
// check that we have a version of the js-sdk which includes initCrypto
if (this.matrixClient.initCrypto) {
await this.matrixClient.initCrypto();
}
} catch(e) {
// this can happen for a number of reasons, the most likely being
// that the olm library was missing. It's not fatal.
console.warn("Unable to initialise e2e: " + e);
}
const opts = utils.deepCopy(this.opts); const opts = utils.deepCopy(this.opts);
// the react sdk doesn't work without this, so don't allow // the react sdk doesn't work without this, so don't allow
opts.pendingEventOrdering = "detached"; opts.pendingEventOrdering = "detached";

View file

@ -301,51 +301,54 @@ const commands = {
const deviceId = matches[2]; const deviceId = matches[2];
const fingerprint = matches[3]; const fingerprint = matches[3];
const device = MatrixClientPeg.get().getStoredDevice(userId, deviceId); return success(
if (!device) { // Promise.resolve to handle transition from static result to promise; can be removed
return reject(_t(`Unknown (user, device) pair:`) + ` (${userId}, ${deviceId})`); // in future
} Promise.resolve(MatrixClientPeg.get().getStoredDevice(userId, deviceId)).then((device) => {
if (!device) {
throw new Error(_t(`Unknown (user, device) pair:`) + ` (${userId}, ${deviceId})`);
}
if (device.isVerified()) { if (device.isVerified()) {
if (device.getFingerprint() === fingerprint) { if (device.getFingerprint() === fingerprint) {
return reject(_t(`Device already verified!`)); throw new Error(_t(`Device already verified!`));
} else { } else {
return reject(_t(`WARNING: Device already verified, but keys do NOT MATCH!`)); throw new Error(_t(`WARNING: Device already verified, but keys do NOT MATCH!`));
} }
} }
if (device.getFingerprint() === fingerprint) { if (device.getFingerprint() !== fingerprint) {
MatrixClientPeg.get().setDeviceVerified( const fprint = device.getFingerprint();
userId, deviceId, true, throw new Error(
); _t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' +
' %(deviceId)s is "%(fprint)s" which does not match the provided key' +
' "%(fingerprint)s". This could mean your communications are being intercepted!',
{deviceId: deviceId, fprint: fprint, userId: userId, fingerprint: fingerprint}));
}
// Tell the user we verified everything! return MatrixClientPeg.get().setDeviceVerified(
const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog"); userId, deviceId, true,
Modal.createDialog(QuestionDialog, { );
title: _t("Verified key"), }).then(() => {
description: ( // Tell the user we verified everything
<div> const QuestionDialog = sdk.getComponent("dialogs.QuestionDialog");
<p> Modal.createDialog(QuestionDialog, {
{ title: _t("Verified key"),
_t("The signing key you provided matches the signing key you received " + description: (
"from %(userId)s's device %(deviceId)s. Device marked as verified.", <div>
{userId: userId, deviceId: deviceId}) <p>
} {
</p> _t("The signing key you provided matches the signing key you received " +
</div> "from %(userId)s's device %(deviceId)s. Device marked as verified.",
), {userId: userId, deviceId: deviceId})
hasCancelButton: false, }
}); </p>
</div>
return success(); ),
} else { hasCancelButton: false,
const fprint = device.getFingerprint(); });
return reject( }),
_t('WARNING: KEY VERIFICATION FAILED! The signing key for %(userId)s and device' + );
' %(deviceId)s is "%(fprint)s" which does not match the provided key' +
' "%(fingerprint)s". This could mean your communications are being intercepted!',
{deviceId: deviceId, fprint: fprint, userId: userId, fingerprint: fingerprint}));
}
} }
} }
return reject(this.getUsage()); return reject(this.getUsage());

View file

@ -28,23 +28,31 @@ module.exports = React.createClass({
}, },
getInitialState: function() { getInitialState: function() {
return { device: this.refreshDevice() }; return { device: null };
}, },
componentWillMount: function() { componentWillMount: function() {
this._unmounted = false; this._unmounted = false;
var client = MatrixClientPeg.get(); var client = MatrixClientPeg.get();
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
// no need to redownload keys if we already have the device // first try to load the device from our store.
if (this.state.device) { //
return; this.refreshDevice().then((dev) => {
} if (dev) {
client.downloadKeys([this.props.event.getSender()], true).done(()=>{ return dev;
}
// tell the client to try to refresh the device list for this user
return client.downloadKeys([this.props.event.getSender()], true).then(() => {
return this.refreshDevice();
});
}).then((dev) => {
if (this._unmounted) { if (this._unmounted) {
return; return;
} }
this.setState({ device: this.refreshDevice() });
this.setState({ device: dev });
client.on("deviceVerificationChanged", this.onDeviceVerificationChanged);
}, (err)=>{ }, (err)=>{
console.log("Error downloading devices", err); console.log("Error downloading devices", err);
}); });
@ -59,12 +67,16 @@ module.exports = React.createClass({
}, },
refreshDevice: function() { refreshDevice: function() {
return MatrixClientPeg.get().getEventSenderDeviceInfo(this.props.event); // Promise.resolve to handle transition from static result to promise; can be removed
// in future
return Promise.resolve(MatrixClientPeg.get().getEventSenderDeviceInfo(this.props.event));
}, },
onDeviceVerificationChanged: function(userId, device) { onDeviceVerificationChanged: function(userId, device) {
if (userId == this.props.event.getSender()) { if (userId == this.props.event.getSender()) {
this.setState({ device: this.refreshDevice() }); this.refreshDevice().then((dev) => {
this.setState({ device: dev });
});
} }
}, },

View file

@ -193,13 +193,12 @@ module.exports = withMatrixClient(React.createClass({
} }
}, },
_verifyEvent: function(mxEvent) { _verifyEvent: async function(mxEvent) {
var verified = null; if (!mxEvent.isEncrypted()) {
return;
if (mxEvent.isEncrypted()) {
verified = this.props.matrixClient.isEventSenderVerified(mxEvent);
} }
const verified = await this.props.matrixClient.isEventSenderVerified(mxEvent);
this.setState({ this.setState({
verified: verified verified: verified
}); });

View file

@ -136,8 +136,12 @@ module.exports = withMatrixClient(React.createClass({
if (userId == this.props.member.userId) { if (userId == this.props.member.userId) {
// no need to re-download the whole thing; just update our copy of // no need to re-download the whole thing; just update our copy of
// the list. // the list.
var devices = this.props.matrixClient.getStoredDevicesForUser(userId);
this.setState({devices: devices}); // Promise.resolve to handle transition from static result to promise; can be removed
// in future
Promise.resolve(this.props.matrixClient.getStoredDevicesForUser(userId)).then((devices) => {
this.setState({devices: devices});
});
} }
}, },
@ -204,14 +208,15 @@ module.exports = withMatrixClient(React.createClass({
var client = this.props.matrixClient; var client = this.props.matrixClient;
var self = this; var self = this;
client.downloadKeys([member.userId], true).finally(function() { client.downloadKeys([member.userId], true).then(() => {
return client.getStoredDevicesForUser(member.userId);
}).finally(function() {
self._cancelDeviceList = null; self._cancelDeviceList = null;
}).done(function() { }).done(function(devices) {
if (cancelled) { if (cancelled) {
// we got cancelled - presumably a different user now // we got cancelled - presumably a different user now
return; return;
} }
var devices = client.getStoredDevicesForUser(member.userId);
self._disambiguateDevices(devices); self._disambiguateDevices(devices);
self.setState({devicesLoading: false, devices: devices}); self.setState({devicesLoading: false, devices: devices});
}, function(err) { }, function(err) {