You've already forked matrix-js-sdk
mirror of
https://github.com/matrix-org/matrix-js-sdk.git
synced 2025-08-09 10:22:46 +03:00
experimental support for warning users when unknown devices show up in a room.
hopefully a step towards fixing https://github.com/vector-im/riot-web/issues/2143
This commit is contained in:
@@ -157,6 +157,21 @@ module.exports.DecryptionError = function(msg) {
|
|||||||
};
|
};
|
||||||
utils.inherits(module.exports.DecryptionError, Error);
|
utils.inherits(module.exports.DecryptionError, Error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Exception thrown specifically when we want to warn the user to consider
|
||||||
|
* the security of their conversation before continuing
|
||||||
|
*
|
||||||
|
* @constructor
|
||||||
|
* @param {string} msg message describing the problem
|
||||||
|
* @extends Error
|
||||||
|
*/
|
||||||
|
module.exports.UnknownDeviceError = function(msg, devices) {
|
||||||
|
this.name = "UnknownDeviceError";
|
||||||
|
this.message = msg;
|
||||||
|
this.devices = devices;
|
||||||
|
};
|
||||||
|
utils.inherits(module.exports.UnknownDeviceError, Error);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Registers an encryption/decryption class for a particular algorithm
|
* Registers an encryption/decryption class for a particular algorithm
|
||||||
*
|
*
|
||||||
|
@@ -388,6 +388,10 @@ MegolmEncryption.prototype._shareKeyWithDevices = function(session, devicesByUse
|
|||||||
MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
||||||
const self = this;
|
const self = this;
|
||||||
return this._getDevicesInRoom(room).then(function(devicesInRoom) {
|
return this._getDevicesInRoom(room).then(function(devicesInRoom) {
|
||||||
|
// check if any of these devices are not yet known to the user.
|
||||||
|
// if so, warn the user so they can verify or ignore.
|
||||||
|
self._checkForUnknownDevices(devicesInRoom);
|
||||||
|
|
||||||
return self._ensureOutboundSession(devicesInRoom);
|
return self._ensureOutboundSession(devicesInRoom);
|
||||||
}).then(function(session) {
|
}).then(function(session) {
|
||||||
const payloadJson = {
|
const payloadJson = {
|
||||||
@@ -415,6 +419,38 @@ MegolmEncryption.prototype.encryptMessage = function(room, eventType, content) {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the devices we're about to send to and see if any are entirely
|
||||||
|
* unknown to the user. If so, warn the user, and mark them as known to
|
||||||
|
* give the user a chance to go verify them before re-sending this message.
|
||||||
|
*/
|
||||||
|
MegolmEncryption.prototype._checkForUnknownDevices = function(devicesInRoom) {
|
||||||
|
const unknownDevices = {};
|
||||||
|
|
||||||
|
Object.keys(devicesInRoom).forEach(userId=>{
|
||||||
|
Object.keys(devicesInRoom[userId]).forEach(deviceId=>{
|
||||||
|
const device = devicesInRoom[userId][deviceId];
|
||||||
|
if (device.isUnverified() && !device.isKnown()) {
|
||||||
|
// mark the devices as known to the user, given we're about to
|
||||||
|
// yell at them.
|
||||||
|
//this._crypto.setDeviceVerification(userId, device.deviceId,
|
||||||
|
// undefined, undefined, true);
|
||||||
|
if (!unknownDevices[userId]) {
|
||||||
|
unknownDevices[userId] = {};
|
||||||
|
}
|
||||||
|
unknownDevices[userId][deviceId] = device;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (Object.keys(unknownDevices).length) {
|
||||||
|
// it'd be kind to pass unknownDevices up to the user in this error
|
||||||
|
throw new base.UnknownDeviceError(
|
||||||
|
"This room contains unknown devices which have not been verified. " +
|
||||||
|
"We strongly recommend you verify them before continuing.", unknownDevices);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the list of unblocked devices for all users in the room
|
* Get the list of unblocked devices for all users in the room
|
||||||
*
|
*
|
||||||
@@ -433,6 +469,9 @@ MegolmEncryption.prototype._getDevicesInRoom = function(room) {
|
|||||||
// have a list of the user's devices, then we already share an e2e room
|
// have a list of the user's devices, then we already share an e2e room
|
||||||
// with them, which means that they will have announced any new devices via
|
// with them, which means that they will have announced any new devices via
|
||||||
// an m.new_device.
|
// an m.new_device.
|
||||||
|
//
|
||||||
|
// XXX: what if the cache is stale, and the user left the room we had in common
|
||||||
|
// and then added new devices before joining this one? --Matthew
|
||||||
return this._crypto.downloadKeys(roomMembers, false).then(function(devices) {
|
return this._crypto.downloadKeys(roomMembers, false).then(function(devices) {
|
||||||
// remove any blocked devices
|
// remove any blocked devices
|
||||||
for (const userId in devices) {
|
for (const userId in devices) {
|
||||||
|
@@ -34,7 +34,11 @@ limitations under the License.
|
|||||||
* <key type>:<id> -> <base64-encoded key>>
|
* <key type>:<id> -> <base64-encoded key>>
|
||||||
*
|
*
|
||||||
* @property {module:crypto/deviceinfo.DeviceVerification} verified
|
* @property {module:crypto/deviceinfo.DeviceVerification} verified
|
||||||
* whether the device has been verified by the user
|
* whether the device has been verified/blocked by the user
|
||||||
|
*
|
||||||
|
* @property {boolean} known
|
||||||
|
* whether the user knows of this device's existence (useful when warning
|
||||||
|
* the user that a user has added new devices)
|
||||||
*
|
*
|
||||||
* @property {Object} unsigned additional data from the homeserver
|
* @property {Object} unsigned additional data from the homeserver
|
||||||
*
|
*
|
||||||
@@ -50,6 +54,7 @@ function DeviceInfo(deviceId) {
|
|||||||
this.algorithms = [];
|
this.algorithms = [];
|
||||||
this.keys = {};
|
this.keys = {};
|
||||||
this.verified = DeviceVerification.UNVERIFIED;
|
this.verified = DeviceVerification.UNVERIFIED;
|
||||||
|
this.known = false;
|
||||||
this.unsigned = {};
|
this.unsigned = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -130,6 +135,24 @@ DeviceInfo.prototype.isVerified = function() {
|
|||||||
return this.verified == DeviceVerification.VERIFIED;
|
return this.verified == DeviceVerification.VERIFIED;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this device is unverified
|
||||||
|
*
|
||||||
|
* @return {Boolean} true if unverified
|
||||||
|
*/
|
||||||
|
DeviceInfo.prototype.isUnverified = function() {
|
||||||
|
return this.verified == DeviceVerification.UNVERIFIED;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the user knows about this device's existence
|
||||||
|
*
|
||||||
|
* @return {Boolean} true if known
|
||||||
|
*/
|
||||||
|
DeviceInfo.prototype.isKnown = function() {
|
||||||
|
return this.known == true;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @enum
|
* @enum
|
||||||
*/
|
*/
|
||||||
|
@@ -684,8 +684,12 @@ Crypto.prototype.getDeviceByIdentityKey = function(userId, algorithm, sender_key
|
|||||||
*
|
*
|
||||||
* @param {?boolean} blocked whether to mark the device as blocked. Null to
|
* @param {?boolean} blocked whether to mark the device as blocked. Null to
|
||||||
* leave unchanged.
|
* leave unchanged.
|
||||||
|
*
|
||||||
|
* @param {?boolean} known whether to mark that the user has been made aware of
|
||||||
|
* the existence of this device. Null to leave unchanged
|
||||||
*/
|
*/
|
||||||
Crypto.prototype.setDeviceVerification = function(userId, deviceId, verified, blocked) {
|
Crypto.prototype.setDeviceVerification = function(userId, deviceId, verified,
|
||||||
|
blocked, known) {
|
||||||
const devices = this._sessionStore.getEndToEndDevicesForUser(userId);
|
const devices = this._sessionStore.getEndToEndDevicesForUser(userId);
|
||||||
if (!devices || !devices[deviceId]) {
|
if (!devices || !devices[deviceId]) {
|
||||||
throw new Error("Unknown device " + userId + ":" + deviceId);
|
throw new Error("Unknown device " + userId + ":" + deviceId);
|
||||||
@@ -706,10 +710,16 @@ Crypto.prototype.setDeviceVerification = function(userId, deviceId, verified, bl
|
|||||||
verificationStatus = DeviceVerification.UNVERIFIED;
|
verificationStatus = DeviceVerification.UNVERIFIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev.verified === verificationStatus) {
|
let knownStatus = dev.known;
|
||||||
|
if (known !== null) {
|
||||||
|
knownStatus = known;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev.verified === verificationStatus && dev.known === knownStatus) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dev.verified = verificationStatus;
|
dev.verified = verificationStatus;
|
||||||
|
dev.known = knownStatus;
|
||||||
this._sessionStore.storeEndToEndDevicesForUser(userId, devices);
|
this._sessionStore.storeEndToEndDevicesForUser(userId, devices);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user