Add a new active
flag for OMEMO devices.
Instead of deleting devices that are not returned in the device list, set an `active` flag, so that we don't remove the trust setting. Set deactivated devices to `active` when a receive an OMEMO message from it. Also, set omemo_supported to true when we've succesfully decrypted a message.
This commit is contained in:
parent
c32ecb7e82
commit
dd0c0b6c56
164
dist/converse.js
vendored
164
dist/converse.js
vendored
@ -56201,9 +56201,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
initialize() {
|
initialize() {
|
||||||
const _converse = this.__super__._converse;
|
const _converse = this.__super__._converse;
|
||||||
const jid = this.model.get('jid');
|
const jid = this.model.get('jid');
|
||||||
this.devicelist = _converse.devicelists.get(jid) || _converse.devicelists.create({
|
this.devicelist = _converse.devicelists.getDeviceList(jid);
|
||||||
'jid': jid
|
|
||||||
});
|
|
||||||
this.devicelist.devices.on('change:bundle', this.render, this);
|
this.devicelist.devices.on('change:bundle', this.render, this);
|
||||||
this.devicelist.devices.on('change:trusted', this.render, this);
|
this.devicelist.devices.on('change:trusted', this.render, this);
|
||||||
this.devicelist.devices.on('remove', this.render, this);
|
this.devicelist.devices.on('remove', this.render, this);
|
||||||
@ -56276,24 +56274,39 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
_converse.log(`${e.name} ${e.message}`, Strophe.LogLevel.ERROR);
|
_converse.log(`${e.name} ${e.message}`, Strophe.LogLevel.ERROR);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async handleDecryptedWhisperMessage(encrypted, key_and_tag) {
|
||||||
|
const _converse = this.__super__._converse,
|
||||||
|
devicelist = _converse.devicelists.getDeviceList(this.get('jid'));
|
||||||
|
|
||||||
|
this.save('omemo_supported', true);
|
||||||
|
let device = devicelist.get(encrypted.device_id);
|
||||||
|
|
||||||
|
if (!device) {
|
||||||
|
device = devicelist.devices.create({
|
||||||
|
'id': encrypted.device_id,
|
||||||
|
'jid': this.get('jid')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (encrypted.payload) {
|
||||||
|
const key = key_and_tag.slice(0, 16),
|
||||||
|
tag = key_and_tag.slice(16);
|
||||||
|
const result = await this.decryptMessage(_.extend(encrypted, {
|
||||||
|
'key': key,
|
||||||
|
'tag': tag
|
||||||
|
}));
|
||||||
|
device.save('active', true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
decrypt(attrs) {
|
decrypt(attrs) {
|
||||||
const _converse = this.__super__._converse,
|
const _converse = this.__super__._converse,
|
||||||
session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10)); // https://xmpp.org/extensions/xep-0384.html#usecases-receiving
|
session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10)); // https://xmpp.org/extensions/xep-0384.html#usecases-receiving
|
||||||
|
|
||||||
if (attrs.encrypted.prekey === true) {
|
if (attrs.encrypted.prekey === true) {
|
||||||
let plaintext;
|
let plaintext;
|
||||||
return session_cipher.decryptPreKeyWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary').then(key_and_tag => {
|
return session_cipher.decryptPreKeyWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary').then(key_and_tag => this.handleDecryptedWhisperMessage(attrs.encrypted, key_and_tag)).then(pt => {
|
||||||
if (attrs.encrypted.payload) {
|
|
||||||
const key = key_and_tag.slice(0, 16),
|
|
||||||
tag = key_and_tag.slice(16);
|
|
||||||
return this.decryptMessage(_.extend(attrs.encrypted, {
|
|
||||||
'key': key,
|
|
||||||
'tag': tag
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Promise.resolve();
|
|
||||||
}).then(pt => {
|
|
||||||
plaintext = pt;
|
plaintext = pt;
|
||||||
return _converse.omemo_store.generateMissingPreKeys();
|
return _converse.omemo_store.generateMissingPreKeys();
|
||||||
}).then(() => _converse.omemo_store.publishBundle()).then(() => {
|
}).then(() => _converse.omemo_store.publishBundle()).then(() => {
|
||||||
@ -56311,14 +56324,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
return attrs;
|
return attrs;
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return session_cipher.decryptWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary').then(key_and_tag => {
|
return session_cipher.decryptWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary').then(key_and_tag => this.handleDecryptedWhisperMessage(attrs.encrypted, key_and_tag)).then(plaintext => _.extend(attrs, {
|
||||||
const key = key_and_tag.slice(0, 16),
|
|
||||||
tag = key_and_tag.slice(16);
|
|
||||||
return this.decryptMessage(_.extend(attrs.encrypted, {
|
|
||||||
'key': key,
|
|
||||||
'tag': tag
|
|
||||||
}));
|
|
||||||
}).then(plaintext => _.extend(attrs, {
|
|
||||||
'plaintext': plaintext
|
'plaintext': plaintext
|
||||||
})).catch(e => {
|
})).catch(e => {
|
||||||
this.reportDecryptionError(e);
|
this.reportDecryptionError(e);
|
||||||
@ -56717,7 +56723,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
// devices associated with the contact, the result of this
|
// devices associated with the contact, the result of this
|
||||||
// concatenation is encrypted using the corresponding
|
// concatenation is encrypted using the corresponding
|
||||||
// long-standing SignalProtocol session.
|
// long-standing SignalProtocol session.
|
||||||
const promises = devices.filter(device => device.get('trusted') != UNTRUSTED).map(device => chatbox.encryptKey(obj.key_and_tag, device));
|
const promises = devices.filter(device => device.get('trusted') != UNTRUSTED && device.get('active')).map(device => chatbox.encryptKey(obj.key_and_tag, device));
|
||||||
return Promise.all(promises).then(dicts => addKeysToMessageStanza(stanza, dicts, obj.iv)).then(stanza => {
|
return Promise.all(promises).then(dicts => addKeysToMessageStanza(stanza, dicts, obj.iv)).then(stanza => {
|
||||||
stanza.c('payload').t(obj.payload).up().up();
|
stanza.c('payload').t(obj.payload).up().up();
|
||||||
stanza.c('store', {
|
stanza.c('store', {
|
||||||
@ -57013,7 +57019,8 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
});
|
});
|
||||||
_converse.Device = Backbone.Model.extend({
|
_converse.Device = Backbone.Model.extend({
|
||||||
defaults: {
|
defaults: {
|
||||||
'trusted': UNDECIDED
|
'trusted': UNDECIDED,
|
||||||
|
'active': true
|
||||||
},
|
},
|
||||||
|
|
||||||
getRandomPreKey() {
|
getRandomPreKey() {
|
||||||
@ -57066,6 +57073,12 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
_converse.Devices = Backbone.Collection.extend({
|
_converse.Devices = Backbone.Collection.extend({
|
||||||
model: _converse.Device
|
model: _converse.Device
|
||||||
});
|
});
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
* @namespace _converse.DeviceList
|
||||||
|
* @memberOf _converse
|
||||||
|
*/
|
||||||
|
|
||||||
_converse.DeviceList = Backbone.Model.extend({
|
_converse.DeviceList = Backbone.Model.extend({
|
||||||
idAttribute: 'jid',
|
idAttribute: 'jid',
|
||||||
|
|
||||||
@ -57079,35 +57092,32 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
this.fetchDevices();
|
this.fetchDevices();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async onDevicesFound(collection) {
|
||||||
|
if (collection.length === 0) {
|
||||||
|
let ids;
|
||||||
|
|
||||||
|
try {
|
||||||
|
ids = await this.fetchDevicesFromServer();
|
||||||
|
} catch (e) {
|
||||||
|
_converse.log(`Could not fetch devices for ${this.get('jid')}`);
|
||||||
|
|
||||||
|
_converse.log(e, Strophe.LogLevel.ERROR);
|
||||||
|
|
||||||
|
this.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.get('jid') === _converse.bare_jid) {
|
||||||
|
await this.publishCurrentDevice(ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
fetchDevices() {
|
fetchDevices() {
|
||||||
if (_.isUndefined(this._devices_promise)) {
|
if (_.isUndefined(this._devices_promise)) {
|
||||||
this._devices_promise = new Promise(resolve => {
|
this._devices_promise = new Promise(resolve => {
|
||||||
this.devices.fetch({
|
this.devices.fetch({
|
||||||
'success': async collection => {
|
'success': _.flow(c => this.onDevicesFound(c), resolve),
|
||||||
if (collection.length === 0) {
|
'error': _.flow(_.partial(_converse.log, _, Strophe.LogLevel.ERROR), resolve)
|
||||||
let ids;
|
|
||||||
|
|
||||||
try {
|
|
||||||
ids = await this.fetchDevicesFromServer();
|
|
||||||
} catch (e) {
|
|
||||||
_converse.log(`Could not fetch devices for ${this.get('jid')}`);
|
|
||||||
|
|
||||||
_converse.log(e, Strophe.LogLevel.ERROR);
|
|
||||||
|
|
||||||
this.destroy();
|
|
||||||
return resolve(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.publishCurrentDevice(ids);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve();
|
|
||||||
},
|
|
||||||
'error': e => {
|
|
||||||
_converse.log(e, Strophe.LogLevel.ERROR);
|
|
||||||
|
|
||||||
resolve(e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -57117,8 +57127,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
|
|
||||||
async publishCurrentDevice(device_ids) {
|
async publishCurrentDevice(device_ids) {
|
||||||
if (this.get('jid') !== _converse.bare_jid) {
|
if (this.get('jid') !== _converse.bare_jid) {
|
||||||
// We only publish for ourselves.
|
return; // We only publish for ourselves.
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await restoreOMEMOSession();
|
await restoreOMEMOSession();
|
||||||
@ -57172,7 +57181,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
const item = $build('item').c('list', {
|
const item = $build('item').c('list', {
|
||||||
'xmlns': Strophe.NS.OMEMO
|
'xmlns': Strophe.NS.OMEMO
|
||||||
});
|
});
|
||||||
this.devices.each(d => item.c('device', {
|
this.devices.filter(d => d.get('active')).forEach(d => item.c('device', {
|
||||||
'id': d.get('id')
|
'id': d.get('id')
|
||||||
}).up());
|
}).up());
|
||||||
const options = {
|
const options = {
|
||||||
@ -57192,8 +57201,28 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
* @namespace _converse.DeviceLists
|
||||||
|
* @memberOf _converse
|
||||||
|
*/
|
||||||
|
|
||||||
_converse.DeviceLists = Backbone.Collection.extend({
|
_converse.DeviceLists = Backbone.Collection.extend({
|
||||||
model: _converse.DeviceList
|
model: _converse.DeviceList,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the {@link _converse.DeviceList} for a particular JID.
|
||||||
|
* The device list will be created if it doesn't exist already.
|
||||||
|
* @private
|
||||||
|
* @method _converse.DeviceLists#getDeviceList
|
||||||
|
* @param { String } jid - The Jabber ID for which the device list will be returned.
|
||||||
|
*/
|
||||||
|
getDeviceList(jid) {
|
||||||
|
return this.get(jid) || this.create({
|
||||||
|
'jid': jid
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
function fetchDeviceLists() {
|
function fetchDeviceLists() {
|
||||||
@ -57227,9 +57256,7 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
const device_id = items_el.getAttribute('node').split(':')[1],
|
const device_id = items_el.getAttribute('node').split(':')[1],
|
||||||
jid = stanza.getAttribute('from'),
|
jid = stanza.getAttribute('from'),
|
||||||
bundle_el = sizzle(`item > bundle`, items_el).pop(),
|
bundle_el = sizzle(`item > bundle`, items_el).pop(),
|
||||||
devicelist = _converse.devicelists.get(jid) || _converse.devicelists.create({
|
devicelist = _converse.devicelists.getDeviceList(jid),
|
||||||
'jid': jid
|
|
||||||
}),
|
|
||||||
device = devicelist.devices.get(device_id) || devicelist.devices.create({
|
device = devicelist.devices.get(device_id) || devicelist.devices.create({
|
||||||
'id': device_id,
|
'id': device_id,
|
||||||
'jid': jid
|
'jid': jid
|
||||||
@ -57250,23 +57277,24 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
const device_ids = _.map(sizzle(`item list[xmlns="${Strophe.NS.OMEMO}"] device`, items_el), device => device.getAttribute('id'));
|
const device_ids = _.map(sizzle(`item list[xmlns="${Strophe.NS.OMEMO}"] device`, items_el), device => device.getAttribute('id'));
|
||||||
|
|
||||||
const jid = stanza.getAttribute('from'),
|
const jid = stanza.getAttribute('from'),
|
||||||
devicelist = _converse.devicelists.get(jid) || _converse.devicelists.create({
|
devicelist = _converse.devicelists.getDeviceList(jid),
|
||||||
'jid': jid
|
|
||||||
}),
|
|
||||||
devices = devicelist.devices,
|
devices = devicelist.devices,
|
||||||
removed_ids = _.difference(devices.pluck('id'), device_ids);
|
removed_ids = _.difference(devices.pluck('id'), device_ids);
|
||||||
|
|
||||||
_.forEach(removed_ids, id => {
|
_.forEach(removed_ids, id => {
|
||||||
if (jid === _converse.bare_jid && id === _converse.omemo_store.get('device_id')) {
|
if (jid === _converse.bare_jid && id === _converse.omemo_store.get('device_id')) {
|
||||||
// We don't remove the current device
|
return; // We don't set the current device as inactive
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
devices.get(id).destroy();
|
devices.get(id).save('active', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
_.forEach(device_ids, device_id => {
|
_.forEach(device_ids, device_id => {
|
||||||
if (!devices.get(device_id)) {
|
const device = devices.get(device_id);
|
||||||
|
|
||||||
|
if (device) {
|
||||||
|
device.save('active', true);
|
||||||
|
} else {
|
||||||
devices.create({
|
devices.create({
|
||||||
'id': device_id,
|
'id': device_id,
|
||||||
'jid': jid
|
'jid': jid
|
||||||
@ -57274,10 +57302,10 @@ _converse_headless_converse_core__WEBPACK_IMPORTED_MODULE_0__["default"].plugins
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (Strophe.getBareJidFromJid(jid) === _converse.bare_jid) {
|
if (u.isSameBareJID(jid, _converse.bare_jid)) {
|
||||||
// Make sure our own device is on the list (i.e. if it was
|
// Make sure our own device is on the list
|
||||||
// removed, add it again.
|
// (i.e. if it was removed, add it again).
|
||||||
_converse.devicelists.get(_converse.bare_jid).publishCurrentDevice(device_ids);
|
devicelist.publishCurrentDevice(device_ids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -701,8 +701,11 @@
|
|||||||
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
_converse.connection._dataRecv(test_utils.createRequest(stanza));
|
||||||
|
|
||||||
expect(_converse.devicelists.length).toBe(2);
|
expect(_converse.devicelists.length).toBe(2);
|
||||||
expect(devices.length).toBe(2);
|
expect(devices.length).toBe(3);
|
||||||
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('4223,4224');
|
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('1234,4223,4224');
|
||||||
|
expect(devices.get('1234').get('active')).toBe(false);
|
||||||
|
expect(devices.get('4223').get('active')).toBe(true);
|
||||||
|
expect(devices.get('4224').get('active')).toBe(true);
|
||||||
|
|
||||||
// Check that own devicelist gets updated
|
// Check that own devicelist gets updated
|
||||||
stanza = $msg({
|
stanza = $msg({
|
||||||
@ -723,6 +726,9 @@
|
|||||||
devices = _converse.devicelists.get(_converse.bare_jid).devices;
|
devices = _converse.devicelists.get(_converse.bare_jid).devices;
|
||||||
expect(devices.length).toBe(3);
|
expect(devices.length).toBe(3);
|
||||||
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,555,777');
|
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,555,777');
|
||||||
|
expect(devices.get('123456789').get('active')).toBe(true);
|
||||||
|
expect(devices.get('555').get('active')).toBe(true);
|
||||||
|
expect(devices.get('777').get('active')).toBe(true);
|
||||||
|
|
||||||
_converse.connection.IQ_stanzas = [];
|
_converse.connection.IQ_stanzas = [];
|
||||||
|
|
||||||
@ -769,8 +775,12 @@
|
|||||||
devices = _converse.devicelists.get(_converse.bare_jid).devices;
|
devices = _converse.devicelists.get(_converse.bare_jid).devices;
|
||||||
// The device id for this device (123456789) was also generated and added to the list,
|
// The device id for this device (123456789) was also generated and added to the list,
|
||||||
// which is why we have 2 devices now.
|
// which is why we have 2 devices now.
|
||||||
expect(devices.length).toBe(2);
|
expect(devices.length).toBe(4);
|
||||||
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,444');
|
expect(_.map(devices.models, 'attributes.id').sort().join()).toBe('123456789,444,555,777');
|
||||||
|
expect(devices.get('123456789').get('active')).toBe(true);
|
||||||
|
expect(devices.get('444').get('active')).toBe(true);
|
||||||
|
expect(devices.get('555').get('active')).toBe(false);
|
||||||
|
expect(devices.get('777').get('active')).toBe(false);
|
||||||
done();
|
done();
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -154,7 +154,7 @@ converse.plugins.add('converse-omemo', {
|
|||||||
initialize () {
|
initialize () {
|
||||||
const { _converse } = this.__super__;
|
const { _converse } = this.__super__;
|
||||||
const jid = this.model.get('jid');
|
const jid = this.model.get('jid');
|
||||||
this.devicelist = _converse.devicelists.get(jid) || _converse.devicelists.create({'jid': jid});
|
this.devicelist = _converse.devicelists.getDeviceList(jid);
|
||||||
this.devicelist.devices.on('change:bundle', this.render, this);
|
this.devicelist.devices.on('change:bundle', this.render, this);
|
||||||
this.devicelist.devices.on('change:trusted', this.render, this);
|
this.devicelist.devices.on('change:trusted', this.render, this);
|
||||||
this.devicelist.devices.on('remove', this.render, this);
|
this.devicelist.devices.on('remove', this.render, this);
|
||||||
@ -229,6 +229,24 @@ converse.plugins.add('converse-omemo', {
|
|||||||
_converse.log(`${e.name} ${e.message}`, Strophe.LogLevel.ERROR);
|
_converse.log(`${e.name} ${e.message}`, Strophe.LogLevel.ERROR);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
async handleDecryptedWhisperMessage (encrypted, key_and_tag) {
|
||||||
|
const { _converse } = this.__super__,
|
||||||
|
devicelist = _converse.devicelists.getDeviceList(this.get('jid'));
|
||||||
|
|
||||||
|
this.save('omemo_supported', true);
|
||||||
|
let device = devicelist.get(encrypted.device_id);
|
||||||
|
if (!device) {
|
||||||
|
device = devicelist.devices.create({'id': encrypted.device_id, 'jid': this.get('jid')});
|
||||||
|
}
|
||||||
|
if (encrypted.payload) {
|
||||||
|
const key = key_and_tag.slice(0, 16),
|
||||||
|
tag = key_and_tag.slice(16);
|
||||||
|
const result = await this.decryptMessage(_.extend(encrypted, {'key': key, 'tag': tag}));
|
||||||
|
device.save('active', true);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
decrypt (attrs) {
|
decrypt (attrs) {
|
||||||
const { _converse } = this.__super__,
|
const { _converse } = this.__super__,
|
||||||
session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10));
|
session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10));
|
||||||
@ -237,14 +255,8 @@ converse.plugins.add('converse-omemo', {
|
|||||||
if (attrs.encrypted.prekey === true) {
|
if (attrs.encrypted.prekey === true) {
|
||||||
let plaintext;
|
let plaintext;
|
||||||
return session_cipher.decryptPreKeyWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary')
|
return session_cipher.decryptPreKeyWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary')
|
||||||
.then(key_and_tag => {
|
.then(key_and_tag => this.handleDecryptedWhisperMessage(attrs.encrypted, key_and_tag))
|
||||||
if (attrs.encrypted.payload) {
|
.then(pt => {
|
||||||
const key = key_and_tag.slice(0, 16),
|
|
||||||
tag = key_and_tag.slice(16);
|
|
||||||
return this.decryptMessage(_.extend(attrs.encrypted, {'key': key, 'tag': tag}));
|
|
||||||
}
|
|
||||||
return Promise.resolve();
|
|
||||||
}).then(pt => {
|
|
||||||
plaintext = pt;
|
plaintext = pt;
|
||||||
return _converse.omemo_store.generateMissingPreKeys();
|
return _converse.omemo_store.generateMissingPreKeys();
|
||||||
}).then(() => _converse.omemo_store.publishBundle())
|
}).then(() => _converse.omemo_store.publishBundle())
|
||||||
@ -260,15 +272,12 @@ converse.plugins.add('converse-omemo', {
|
|||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
return session_cipher.decryptWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary')
|
return session_cipher.decryptWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary')
|
||||||
.then(key_and_tag => {
|
.then(key_and_tag => this.handleDecryptedWhisperMessage(attrs.encrypted, key_and_tag))
|
||||||
const key = key_and_tag.slice(0, 16),
|
.then(plaintext => _.extend(attrs, {'plaintext': plaintext}))
|
||||||
tag = key_and_tag.slice(16);
|
.catch(e => {
|
||||||
return this.decryptMessage(_.extend(attrs.encrypted, {'key': key, 'tag': tag}));
|
this.reportDecryptionError(e);
|
||||||
}).then(plaintext => _.extend(attrs, {'plaintext': plaintext}))
|
return attrs;
|
||||||
.catch(e => {
|
});
|
||||||
this.reportDecryptionError(e);
|
|
||||||
return attrs;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -625,7 +634,7 @@ converse.plugins.add('converse-omemo', {
|
|||||||
// concatenation is encrypted using the corresponding
|
// concatenation is encrypted using the corresponding
|
||||||
// long-standing SignalProtocol session.
|
// long-standing SignalProtocol session.
|
||||||
const promises = devices
|
const promises = devices
|
||||||
.filter(device => device.get('trusted') != UNTRUSTED)
|
.filter(device => (device.get('trusted') != UNTRUSTED && device.get('active')))
|
||||||
.map(device => chatbox.encryptKey(obj.key_and_tag, device));
|
.map(device => chatbox.encryptKey(obj.key_and_tag, device));
|
||||||
|
|
||||||
return Promise.all(promises)
|
return Promise.all(promises)
|
||||||
@ -890,7 +899,8 @@ converse.plugins.add('converse-omemo', {
|
|||||||
|
|
||||||
_converse.Device = Backbone.Model.extend({
|
_converse.Device = Backbone.Model.extend({
|
||||||
defaults: {
|
defaults: {
|
||||||
'trusted': UNDECIDED
|
'trusted': UNDECIDED,
|
||||||
|
'active': true
|
||||||
},
|
},
|
||||||
|
|
||||||
getRandomPreKey () {
|
getRandomPreKey () {
|
||||||
@ -939,6 +949,11 @@ converse.plugins.add('converse-omemo', {
|
|||||||
model: _converse.Device,
|
model: _converse.Device,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
* @namespace _converse.DeviceList
|
||||||
|
* @memberOf _converse
|
||||||
|
*/
|
||||||
_converse.DeviceList = Backbone.Model.extend({
|
_converse.DeviceList = Backbone.Model.extend({
|
||||||
idAttribute: 'jid',
|
idAttribute: 'jid',
|
||||||
|
|
||||||
@ -1016,7 +1031,7 @@ converse.plugins.add('converse-omemo', {
|
|||||||
|
|
||||||
publishDevices () {
|
publishDevices () {
|
||||||
const item = $build('item').c('list', {'xmlns': Strophe.NS.OMEMO})
|
const item = $build('item').c('list', {'xmlns': Strophe.NS.OMEMO})
|
||||||
this.devices.each(d => item.c('device', {'id': d.get('id')}).up());
|
this.devices.filter(d => d.get('active')).forEach(d => item.c('device', {'id': d.get('id')}).up());
|
||||||
const options = {'pubsub#access_model': 'open'};
|
const options = {'pubsub#access_model': 'open'};
|
||||||
return _converse.api.pubsub.publish(null, Strophe.NS.OMEMO_DEVICELIST, item, options, false);
|
return _converse.api.pubsub.publish(null, Strophe.NS.OMEMO_DEVICELIST, item, options, false);
|
||||||
},
|
},
|
||||||
@ -1030,8 +1045,23 @@ converse.plugins.add('converse-omemo', {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @class
|
||||||
|
* @namespace _converse.DeviceLists
|
||||||
|
* @memberOf _converse
|
||||||
|
*/
|
||||||
_converse.DeviceLists = Backbone.Collection.extend({
|
_converse.DeviceLists = Backbone.Collection.extend({
|
||||||
model: _converse.DeviceList,
|
model: _converse.DeviceList,
|
||||||
|
/**
|
||||||
|
* Returns the {@link _converse.DeviceList} for a particular JID.
|
||||||
|
* The device list will be created if it doesn't exist already.
|
||||||
|
* @private
|
||||||
|
* @method _converse.DeviceLists#getDeviceList
|
||||||
|
* @param { String } jid - The Jabber ID for which the device list will be returned.
|
||||||
|
*/
|
||||||
|
getDeviceList (jid) {
|
||||||
|
return this.get(jid) || this.create({'jid': jid});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@ -1056,7 +1086,7 @@ converse.plugins.add('converse-omemo', {
|
|||||||
const device_id = items_el.getAttribute('node').split(':')[1],
|
const device_id = items_el.getAttribute('node').split(':')[1],
|
||||||
jid = stanza.getAttribute('from'),
|
jid = stanza.getAttribute('from'),
|
||||||
bundle_el = sizzle(`item > bundle`, items_el).pop(),
|
bundle_el = sizzle(`item > bundle`, items_el).pop(),
|
||||||
devicelist = _converse.devicelists.get(jid) || _converse.devicelists.create({'jid': jid}),
|
devicelist = _converse.devicelists.getDeviceList(jid),
|
||||||
device = devicelist.devices.get(device_id) || devicelist.devices.create({'id': device_id, 'jid': jid});
|
device = devicelist.devices.get(device_id) || devicelist.devices.create({'id': device_id, 'jid': jid});
|
||||||
device.save({'bundle': parseBundle(bundle_el)});
|
device.save({'bundle': parseBundle(bundle_el)});
|
||||||
}
|
}
|
||||||
@ -1071,27 +1101,28 @@ converse.plugins.add('converse-omemo', {
|
|||||||
(device) => device.getAttribute('id')
|
(device) => device.getAttribute('id')
|
||||||
);
|
);
|
||||||
const jid = stanza.getAttribute('from'),
|
const jid = stanza.getAttribute('from'),
|
||||||
devicelist = _converse.devicelists.get(jid) || _converse.devicelists.create({'jid': jid}),
|
devicelist = _converse.devicelists.getDeviceList(jid),
|
||||||
devices = devicelist.devices,
|
devices = devicelist.devices,
|
||||||
removed_ids = _.difference(devices.pluck('id'), device_ids);
|
removed_ids = _.difference(devices.pluck('id'), device_ids);
|
||||||
|
|
||||||
_.forEach(removed_ids, (id) => {
|
_.forEach(removed_ids, (id) => {
|
||||||
if (jid === _converse.bare_jid && id === _converse.omemo_store.get('device_id')) {
|
if (jid === _converse.bare_jid && id === _converse.omemo_store.get('device_id')) {
|
||||||
// We don't remove the current device
|
return // We don't set the current device as inactive
|
||||||
return
|
|
||||||
}
|
}
|
||||||
devices.get(id).destroy();
|
devices.get(id).save('active', false);
|
||||||
});
|
});
|
||||||
|
|
||||||
_.forEach(device_ids, (device_id) => {
|
_.forEach(device_ids, (device_id) => {
|
||||||
if (!devices.get(device_id)) {
|
const device = devices.get(device_id);
|
||||||
|
if (device) {
|
||||||
|
device.save('active', true);
|
||||||
|
} else {
|
||||||
devices.create({'id': device_id, 'jid': jid})
|
devices.create({'id': device_id, 'jid': jid})
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (Strophe.getBareJidFromJid(jid) === _converse.bare_jid) {
|
if (u.isSameBareJID(jid, _converse.bare_jid)) {
|
||||||
// Make sure our own device is on the list (i.e. if it was
|
// Make sure our own device is on the list
|
||||||
// removed, add it again.
|
// (i.e. if it was removed, add it again).
|
||||||
_converse.devicelists.get(_converse.bare_jid).publishCurrentDevice(device_ids);
|
devicelist.publishCurrentDevice(device_ids);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user