Test that own devices (from other clients) get included

when sending out encrypted messages.

updates #497
This commit is contained in:
JC Brand 2018-07-28 16:36:23 +02:00
parent a3593dbc7d
commit e774e9d1af
3 changed files with 86 additions and 23 deletions

View File

@ -10,7 +10,7 @@
describe("The OMEMO module", function() {
it("enables encrypted messages to be sent",
it("enables encrypted messages to be sent and received",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched'], {},
function (done, _converse) {
@ -83,7 +83,9 @@
return _.filter(
_converse.connection.IQ_stanzas,
(iq) => {
const node = iq.nodeTree.querySelector('iq[to="'+contact_jid+'"] items[node="eu.siacs.conversations.axolotl.bundles:555"]');
const node = iq.nodeTree.querySelector(
'iq[to="'+contact_jid+'"] items[node="eu.siacs.conversations.axolotl.bundles:555"]'
);
if (node) { iq_stanza = iq.nodeTree; }
return node;
}).length;
@ -106,6 +108,36 @@
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1001')).up()
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1002')).up()
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1003'));
_converse.connection._dataRecv(test_utils.createRequest(stanza));
return test_utils.waitUntil(() => {
return _.filter(
_converse.connection.IQ_stanzas,
(iq) => {
const node = iq.nodeTree.querySelector(
'iq[to="'+_converse.bare_jid+'"] items[node="eu.siacs.conversations.axolotl.bundles:482886413b977930064a5888b92134fe"]'
);
if (node) { iq_stanza = iq.nodeTree; }
return node;
}).length;
});
}).then(() => {
const stanza = $iq({
'from': _converse.bare_jid,
'id': iq_stanza.getAttribute('id'),
'to': _converse.bare_jid,
'type': 'result',
}).c('pubsub', {
'xmlns': 'http://jabber.org/protocol/pubsub'
}).c('items', {'node': "eu.siacs.conversations.axolotl.bundles:482886413b977930064a5888b92134fe"})
.c('item')
.c('bundle', {'xmlns': 'eu.siacs.conversations.axolotl'})
.c('signedPreKeyPublic', {'signedPreKeyId': '4223'}).t(btoa('100000')).up()
.c('signedPreKeySignature').t(btoa('200000')).up()
.c('identityKey').t(btoa('300000')).up()
.c('prekeys')
.c('preKeyPublic', {'preKeyId': '1'}).t(btoa('1991')).up()
.c('preKeyPublic', {'preKeyId': '2'}).t(btoa('1992')).up()
.c('preKeyPublic', {'preKeyId': '3'}).t(btoa('1993'));
spyOn(_converse.connection, 'send').and.callFake(stanza => { sent_stanza = stanza });
_converse.connection._dataRecv(test_utils.createRequest(stanza));
@ -117,11 +149,25 @@
`<body>This is an OMEMO encrypted message which your client doesnt seem to support. Find more information on https://conversations.im/omemo</body>`+
`<encrypted xmlns='eu.siacs.conversations.axolotl'>`+
`<header sid='123456789'>`+
`<key>eyJpdiI6IjEyMzQ1In0=</key>`+
`<iv>12345</iv>`+
`<key rid='482886413b977930064a5888b92134fe'>eyJ0eXBlIjoxLCJib2R5IjoiYzFwaDNSNzNYNyIsInJlZ2lzdHJhdGlvbklkIjoiMTMzNyJ9</key>`+
`<key rid='555'>eyJ0eXBlIjoxLCJib2R5IjoiYzFwaDNSNzNYNyIsInJlZ2lzdHJhdGlvbklkIjoiMTMzNyJ9</key>`+
`<iv>${sent_stanza.nodeTree.querySelector('iv').textContent}</iv>`+
`</header>`+
`</encrypted>`+
`</message>`);
// Test reception of an encrypted message
const stanza = $msg({
'from': contact_jid,
'to': _converse.connection.jid,
'type': 'chat',
'id': 'qwerty'
}).c('body').t('This is a fallback message').up()
.c('encrypted', {'xmlns': Strophe.NS.OMEMO})
.c('header', {'sid': '555'})
.c('key', {'rid': _converse.omemo_store.get('device_id')}).t('c1ph3R73X7').up()
.c('iv').t('1234');
_converse.connection._dataRecv(test_utils.createRequest(stanza));
done();
});
}));

View File

@ -92,8 +92,13 @@
const { _converse } = this.__super__;
return new Promise((resolve, reject) => {
_converse.getDevicesForContact(this.get('jid'))
.then((devices) => {
Promise.all(devices.map((device) => device.getBundle()))
.then((their_devices) => {
const device_id = _converse.omemo_store.get('device_id'),
devicelist = _converse.devicelists.get(_converse.bare_jid),
own_devices = devicelist.devices.filter(device => device.get('id') !== device_id),
devices = _.concat(own_devices, their_devices.models);
Promise.all(devices.map(device => device.getBundle()))
.then(() => this.buildSessions(devices))
.then(() => resolve(devices))
.catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
@ -165,22 +170,27 @@
address = new libsignal.SignalProtocolAddress(this.get('jid'), device.get('id')),
sessionCipher = new window.libsignal.SessionCipher(_converse.omemo_store, address);
return sessionCipher.encrypt(plaintext);
return new Promise((resolve, reject) => {
sessionCipher.encrypt(plaintext)
.then(payload => resolve({'payload': payload, 'device': device}))
.catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
});
},
addKeysToMessageStanza (stanza, devices, payloads) {
for (var i in payloads) {
if (Object.prototype.hasOwnProperty.call(payloads, i)) {
const payload = btoa(JSON.stringify(payloads[i]))
const prekey = 3 == parseInt(payloads[i].type, 10)
if (i == payloads.length-1) {
stanza.c('key', {'rid': devices.get('id') }).t(payload)
if (prekey) {
stanza.attrs({'prekey': prekey});
}
stanza.up().c('iv').t(payloads[0].iv).up().up()
} else {
stanza.c('key', {prekey: prekey, rid: devices.get('id') }).t(payload).up()
addKeysToMessageStanza (stanza, dicts, iv) {
for (var i in dicts) {
if (Object.prototype.hasOwnProperty.call(dicts, i)) {
const payload = dicts[i].payload,
device = dicts[i].device,
prekey = 3 == parseInt(payload.type, 10);
stanza.c('key', {'rid': device.get('id') }).t(btoa(JSON.stringify(dicts[i].payload)));
if (prekey) {
stanza.attrs({'prekey': prekey});
}
stanza.up();
if (i == dicts.length-1) {
stanza.c('iv').t(iv).up().up()
}
}
}
@ -213,8 +223,13 @@
// long-standing SignalProtocol session.
// TODO: need to include own devices here as well (and filter out distrusted devices)
const promises = devices.map(device => this.encryptKey(payload.key_str+payload.tag, device));
return Promise.all(promises).then((payloads) => this.addKeysToMessageStanza(stanza, devices, payloads));
const promises = devices
.filter(device => device.get('trusted') != UNTRUSTED)
.map(device => this.encryptKey(payload.key_str+payload.tag, device));
return Promise.all(promises)
.then((dicts) => this.addKeysToMessageStanza(stanza, dicts, payload.iv))
.catch(_.partial(_converse.log, _, Strophe.LogLevel.ERROR));
});
},

View File

@ -16,7 +16,9 @@
this.remoteAddress = remote_address;
this.storage = storage;
this.encrypt = () => Promise.resolve({
'iv': '12345'
'type': 1,
'body': 'c1ph3R73X7',
'registrationId': '1337'
});
},
'SessionBuilder': function (storage, remote_address) {