Commit 7c43d043 authored by JC Brand's avatar JC Brand

Refactor OMEMO.

- Add hooks to the stanza parsers so that plugins can do additional parsing.
- Change ChatBox instance methods to functions and use them for stanza parsing.
- Move encrypt and decrypt messages to `converse.env.omemo`

Apparently, when receving a 1:1 carbon message, a device was wrongly created
for the contact's device list, instead of our own.
parent fce337e3
......@@ -7307,9 +7307,9 @@
},
"dependencies": {
"dot-prop": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.2.0.tgz",
"integrity": "sha512-uEUyaDKoSQ1M4Oq8l45hSE26SnTxL6snNnqvK/VWx5wJhmff5z0FUVJDKDanor/6w3kzE3i7XZOk+7wC0EXr1A==",
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-5.3.0.tgz",
"integrity": "sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==",
"dev": true,
"requires": {
"is-obj": "^2.0.0"
......@@ -11651,9 +11651,9 @@
}
},
"git-url-parse": {
"version": "11.1.3",
"resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.1.3.tgz",
"integrity": "sha512-GPsfwticcu52WQ+eHp0IYkAyaOASgYdtsQDIt4rUp6GbiNt1P9ddrh3O0kQB0eD4UJZszVqNT3+9Zwcg40fywA==",
"version": "11.2.0",
"resolved": "https://registry.npmjs.org/git-url-parse/-/git-url-parse-11.2.0.tgz",
"integrity": "sha512-KPoHZg8v+plarZvto4ruIzzJLFQoRx+sUs5DQSr07By9IBKguVd+e6jwrFR6/TP6xrCJlNV1tPqLO1aREc7O2g==",
"dev": true,
"requires": {
"git-up": "^4.0.0"
......@@ -14712,9 +14712,9 @@
}
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
"version": "2.6.1",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
"dev": true
},
"node-fetch-npm": {
......@@ -21278,9 +21278,9 @@
}
},
"rxjs": {
"version": "6.6.2",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.2.tgz",
"integrity": "sha512-BHdBMVoWC2sL26w//BCu3YzKT4s2jip/WhwsGEDmeKYBhKDZeYezVUnHatYB7L85v5xs0BAQmg6BEYJEKxBabg==",
"version": "6.6.3",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz",
"integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==",
"dev": true,
"requires": {
"tslib": "^1.9.0"
......
/*global mock, converse */
const { $iq, $pres, $msg, _, Strophe } = converse.env;
const { $iq, $pres, $msg, _, omemo, Strophe } = converse.env;
const u = converse.env.utils;
async function deviceListFetched (_converse, jid) {
......@@ -78,15 +78,12 @@ describe("The OMEMO module", function() {
const message = 'This message will be encrypted'
await mock.waitForRoster(_converse, 'current', 1);
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
const view = await mock.openChatBoxFor(_converse, contact_jid);
const payload = await view.model.encryptMessage(message);
const result = await view.model.decryptMessage(payload);
const payload = await omemo.encryptMessage(message);
const result = await omemo.decryptMessage(payload);
expect(result).toBe(message);
done();
}));
it("enables encrypted messages to be sent and received",
mock.initConverse(
['rosterGroupsFetched', 'chatBoxesFetched'], {},
......@@ -182,10 +179,9 @@ describe("The OMEMO module", function() {
`</message>`);
// Test reception of an encrypted message
let obj = await view.model.encryptMessage('This is an encrypted message from the contact')
let obj = await omemo.encryptMessage('This is an encrypted message from the contact')
// XXX: Normally the key will be encrypted via libsignal.
// However, we're mocking libsignal in the tests, so we include
// it as plaintext in the message.
// However, we're mocking libsignal in the tests, so we include it as plaintext in the message.
stanza = $msg({
'from': contact_jid,
'to': _converse.connection.jid,
......@@ -205,7 +201,7 @@ describe("The OMEMO module", function() {
.toBe('This is an encrypted message from the contact');
// #1193 Check for a received message without <body> tag
obj = await view.model.encryptMessage('Another received encrypted message without fallback')
obj = await omemo.encryptMessage('Another received encrypted message without fallback')
stanza = $msg({
'from': contact_jid,
'to': _converse.connection.jid,
......@@ -383,6 +379,9 @@ describe("The OMEMO module", function() {
await u.waitUntil(() => initializedOMEMO(_converse));
await mock.openChatBoxFor(_converse, contact_jid);
let iq_stanza = await u.waitUntil(() => deviceListFetched(_converse, contact_jid));
const my_devicelist = _converse.devicelists.get({'jid': _converse.bare_jid});
expect(my_devicelist.devices.length).toBe(2);
const stanza = $iq({
'from': contact_jid,
'id': iq_stanza.getAttribute('id'),
......@@ -395,14 +394,15 @@ describe("The OMEMO module", function() {
.c('device', {'id': '555'});
_converse.connection._dataRecv(mock.createRequest(stanza));
await u.waitUntil(() => _converse.omemo_store);
const devicelist = _converse.devicelists.get({'jid': contact_jid});
await u.waitUntil(() => devicelist.devices.length === 1);
const contact_devicelist = _converse.devicelists.get({'jid': contact_jid});
await u.waitUntil(() => contact_devicelist.devices.length === 1);
const view = _converse.chatboxviews.get(contact_jid);
view.model.set('omemo_active', true);
// Test reception of an encrypted carbon message
const obj = await view.model.encryptMessage('This is an encrypted carbon message from another device of mine')
const obj = await omemo.encryptMessage('This is an encrypted carbon message from another device of mine')
const carbon = u.toStanza(`
<message xmlns="jabber:client" to="romeo@montague.lit/orchard" from="romeo@montague.lit" type="chat">
<sent xmlns="urn:xmpp:carbons:2">
......@@ -440,10 +440,14 @@ describe("The OMEMO module", function() {
expect(view.el.querySelector('.chat-msg__text').textContent.trim())
.toBe('This is an encrypted carbon message from another device of mine');
expect(devicelist.devices.length).toBe(2);
expect(devicelist.devices.at(0).get('id')).toBe('555');
expect(devicelist.devices.at(1).get('id')).toBe('988349631');
expect(devicelist.devices.get('988349631').get('active')).toBe(true);
expect(contact_devicelist.devices.length).toBe(1);
// Check that the new device id has been added to my devices
expect(my_devicelist.devices.length).toBe(3);
expect(my_devicelist.devices.at(0).get('id')).toBe('482886413b977930064a5888b92134fe');
expect(my_devicelist.devices.at(1).get('id')).toBe('123456789');
expect(my_devicelist.devices.at(2).get('id')).toBe('988349631');
expect(my_devicelist.devices.get('988349631').get('active')).toBe(true);
const textarea = view.el.querySelector('.chat-textarea');
textarea.value = 'This is an encrypted message from this device';
......@@ -601,7 +605,7 @@ describe("The OMEMO module", function() {
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
await u.waitUntil(() => initializedOMEMO(_converse));
const obj = await _converse.ChatBox.prototype.encryptMessage('This is an encrypted message from the contact');
const obj = await omemo.encryptMessage('This is an encrypted message from the contact');
// XXX: Normally the key will be encrypted via libsignal.
// However, we're mocking libsignal in the tests, so we include
// it as plaintext in the message.
......@@ -631,8 +635,8 @@ describe("The OMEMO module", function() {
return generateMissingPreKeys.apply(_converse.omemo_store, arguments);
});
_converse.connection._dataRecv(mock.createRequest(stanza));
let iq_stanza = await u.waitUntil(() => _converse.chatboxviews.get(contact_jid));
iq_stanza = await deviceListFetched(_converse, contact_jid);
let iq_stanza = await deviceListFetched(_converse, contact_jid);
stanza = $iq({
'from': contact_jid,
'id': iq_stanza.getAttribute('id'),
......@@ -688,7 +692,6 @@ describe("The OMEMO module", function() {
done();
}));
it("updates device lists based on PEP messages",
mock.initConverse(
['rosterGroupsFetched'], {'allow_non_roster_messaging': true},
......
This diff is collapsed.
......@@ -457,8 +457,6 @@ converse.plugins.add('converse-chat', {
attrs.stanza && log.error(attrs.stanza);
return log.error(attrs.message);
}
// TODO: move to OMEMO
attrs = attrs.encrypted ? await this.decrypt(attrs) : attrs;
const message = this.getDuplicateMessage(attrs);
if (message) {
this.updateMessage(message, attrs);
......@@ -1215,7 +1213,7 @@ converse.plugins.add('converse-chat', {
}
const has_body = !!sizzle(`body, encrypted[xmlns="${Strophe.NS.OMEMO}"]`, stanza).length;
const chatbox = await api.chats.get(attrs.contact_jid, {'nickname': attrs.nick }, has_body);
chatbox && await chatbox.queueMessage(attrs);
await chatbox?.queueMessage(attrs);
/**
* Triggered when a message stanza is been received and processed.
* @event _converse#message
......
......@@ -1993,8 +1993,6 @@ converse.plugins.add('converse-muc', {
attrs.stanza && log.error(attrs.stanza);
return log.error(attrs.message);
}
// TODO: move to OMEMO
attrs = attrs.encrypted ? await this.decrypt(attrs) : attrs;
const message = this.getDuplicateMessage(attrs);
if (message) {
return this.updateMessage(message, attrs);
......
......@@ -362,7 +362,6 @@ const st = {
}, {});
},
/**
* Parses a passed in message stanza and returns an object of attributes.
* @method st#parseMessage
......@@ -418,7 +417,6 @@ const st = {
);
}
const is_headline = st.isHeadline(stanza);
const is_server_message = st.isServerMessage(stanza);
let contact, contact_jid;
......@@ -534,7 +532,12 @@ const st = {
// We prefer to use one of the XEP-0359 unique and stable stanza IDs
// as the Model id, to avoid duplicates.
attrs['id'] = attrs['origin_id'] || attrs[`stanza_id ${(attrs.from)}`] || u.getUniqueId();
return attrs;
/**
* *Hook* which allows plugins to add additional parsing
* @event _converse#parseMessage
*/
return api.hook('parseMessage', attrs);
},
/**
......@@ -678,7 +681,11 @@ const st = {
}
// We prefer to use one of the XEP-0359 unique and stable stanza IDs as the Model id, to avoid duplicates.
attrs['id'] = attrs['origin_id'] || attrs[`stanza_id ${(attrs.from_muc || attrs.from)}`] || u.getUniqueId();
return attrs;
/**
* *Hook* which allows plugins to add additional parsing
* @event _converse#parseMUCMessage
*/
return api.hook('parseMUCMessage', attrs);
},
/**
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment