Commit f2c283c9 authored by JC Brand's avatar JC Brand

More work on decrypting messages

parent be0eaecf
This diff is collapsed.
...@@ -10,13 +10,30 @@ ...@@ -10,13 +10,30 @@
describe("The OMEMO module", function() { describe("The OMEMO module", function() {
it("adds methods for encrypting and decrypting messages via AES GCM",
mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) {
let iq_stanza, view, sent_stanza;
test_utils.createContacts(_converse, 'current', 1);
_converse.emit('rosterContactsFetched');
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
test_utils.openChatBoxFor(_converse, contact_jid)
.then((view) => view.model.encryptMessage('This message will be encrypted'))
.then((payload) => {
debugger;
return view.model.decryptMessage(payload);
}).then(done);
}));
it("enables encrypted messages to be sent and received", it("enables encrypted messages to be sent and received",
mock.initConverseWithPromises( mock.initConverseWithPromises(
null, ['rosterGroupsFetched', 'chatBoxesFetched'], {}, null, ['rosterGroupsFetched', 'chatBoxesFetched'], {},
function (done, _converse) { function (done, _converse) {
var sent_stanza; let iq_stanza, view, sent_stanza;
let iq_stanza, view;
test_utils.createContacts(_converse, 'current', 1); test_utils.createContacts(_converse, 'current', 1);
_converse.emit('rosterContactsFetched'); _converse.emit('rosterContactsFetched');
const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost'; const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@localhost';
...@@ -143,7 +160,7 @@ ...@@ -143,7 +160,7 @@
spyOn(_converse.connection, 'send').and.callFake(stanza => { sent_stanza = stanza }); spyOn(_converse.connection, 'send').and.callFake(stanza => { sent_stanza = stanza });
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
return test_utils.waitUntil(() => sent_stanza); return test_utils.waitUntil(() => sent_stanza);
}).then(function () { }).then(() => {
expect(sent_stanza.toLocaleString()).toBe( expect(sent_stanza.toLocaleString()).toBe(
`<message from='dummy@localhost/resource' to='max.frankfurter@localhost' `+ `<message from='dummy@localhost/resource' to='max.frankfurter@localhost' `+
`type='chat' id='${sent_stanza.nodeTree.getAttribute('id')}' xmlns='jabber:client'>`+ `type='chat' id='${sent_stanza.nodeTree.getAttribute('id')}' xmlns='jabber:client'>`+
...@@ -154,10 +171,21 @@ ...@@ -154,10 +171,21 @@
`<key rid='555'>eyJ0eXBlIjoxLCJib2R5IjoiYzFwaDNSNzNYNyIsInJlZ2lzdHJhdGlvbklkIjoiMTMzNyJ9</key>`+ `<key rid='555'>eyJ0eXBlIjoxLCJib2R5IjoiYzFwaDNSNzNYNyIsInJlZ2lzdHJhdGlvbklkIjoiMTMzNyJ9</key>`+
`<iv>${sent_stanza.nodeTree.querySelector('iv').textContent}</iv>`+ `<iv>${sent_stanza.nodeTree.querySelector('iv').textContent}</iv>`+
`</header>`+ `</header>`+
`<payload>${sent_stanza.nodeTree.querySelector('payload').textContent}</payload>`+
`</encrypted>`+ `</encrypted>`+
`</message>`); `</message>`);
// Test reception of an encrypted message // Test reception of an encrypted message
return view.model.encryptMessage('This is an encrypted message from the contact')
}).then((payload) => {
// 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.
const key = btoa(JSON.stringify({
'type': 1,
'body': payload.key_str+payload.tag,
'registrationId': '1337'
}));
const stanza = $msg({ const stanza = $msg({
'from': contact_jid, 'from': contact_jid,
'to': _converse.connection.jid, 'to': _converse.connection.jid,
...@@ -166,21 +194,22 @@ ...@@ -166,21 +194,22 @@
}).c('body').t('This is a fallback message').up() }).c('body').t('This is a fallback message').up()
.c('encrypted', {'xmlns': Strophe.NS.OMEMO}) .c('encrypted', {'xmlns': Strophe.NS.OMEMO})
.c('header', {'sid': '555'}) .c('header', {'sid': '555'})
.c('key', {'rid': _converse.omemo_store.get('device_id')}).t('c1ph3R73X7').up() .c('key', {'rid': _converse.omemo_store.get('device_id')}).t(key).up()
.c('iv').t('1234') .c('iv').t(payload.iv)
.up().up() .up().up()
.c('payload').t('M04R-c1ph3R73X7'); .c('payload').t(payload.ciphertext);
_converse.connection._dataRecv(test_utils.createRequest(stanza)); _converse.connection._dataRecv(test_utils.createRequest(stanza));
return test_utils.waitUntil(() => view.model.messages.length > 1);
}).then(() => {
expect(view.model.messages.length).toBe(2); expect(view.model.messages.length).toBe(2);
const last_msg = view.model.messages.at(1), const last_msg = view.model.messages.at(1),
encrypted = last_msg.get('encrypted'); encrypted = last_msg.get('encrypted');
expect(encrypted instanceof Object).toBe(true); expect(encrypted instanceof Object).toBe(true);
expect(encrypted.device_id).toBe('555'); expect(encrypted.device_id).toBe('555');
expect(encrypted.iv).toBe('1234'); expect(encrypted.iv).toBe(btoa('1234'));
expect(encrypted.key).toBe('c1ph3R73X7'); expect(encrypted.key).toBe(btoa('c1ph3R73X7'));
expect(encrypted.payload).toBe('M04R-c1ph3R73X7'); expect(encrypted.payload).toBe(btoa('M04R-c1ph3R73X7'));
done(); done();
}); });
})); }));
......
...@@ -498,25 +498,29 @@ ...@@ -498,25 +498,29 @@
if (spoiler) { if (spoiler) {
attrs.spoiler_hint = spoiler.textContent.length > 0 ? spoiler.textContent : ''; attrs.spoiler_hint = spoiler.textContent.length > 0 ? spoiler.textContent : '';
} }
return attrs; return Promise.resolve(attrs);
}, },
createMessage (message, original_stanza) { createMessage (message, original_stanza) {
/* Create a Backbone.Message object inside this chat box /* Create a Backbone.Message object inside this chat box
* based on the identified message stanza. * based on the identified message stanza.
*/ */
const attrs = this.getMessageAttributesFromStanza(message, original_stanza); return new Promise((resolve, reject) => {
const is_csn = u.isOnlyChatStateNotification(attrs); this.getMessageAttributesFromStanza(message, original_stanza)
if (is_csn && (attrs.is_delayed || (attrs.type === 'groupchat' && Strophe.getResourceFromJid(attrs.from) == this.get('nick')))) { .then((attrs) => {
// XXX: MUC leakage const is_csn = u.isOnlyChatStateNotification(attrs);
// No need showing delayed or our own CSN messages if (is_csn && (attrs.is_delayed || (attrs.type === 'groupchat' && Strophe.getResourceFromJid(attrs.from) == this.get('nick')))) {
return; // XXX: MUC leakage
} else if (!is_csn && !attrs.file && !attrs.message && !attrs.oob_url && attrs.type !== 'error') { // No need showing delayed or our own CSN messages
// TODO: handle <subject> messages (currently being done by ChatRoom) resolve();
return; } else if (!is_csn && !attrs.file && !attrs.message && !attrs.oob_url && attrs.type !== 'error') {
} else { // TODO: handle <subject> messages (currently being done by ChatRoom)
return this.messages.create(attrs); resolve();
} } else {
resolve(this.messages.create(attrs));
}
});
});
}, },
isHidden () { isHidden () {
......
...@@ -129,12 +129,16 @@ ...@@ -129,12 +129,16 @@
// New functions which don't exist yet can also be added. // New functions which don't exist yet can also be added.
ChatBox: { ChatBox: {
getMessageAttributesFromStanza (message, original_stanza) { getMessageAttributesFromStanza (message, original_stanza) {
const attrs = this.__super__.getMessageAttributesFromStanza.apply(this, arguments); return new Promise((resolve, reject) => {
const archive_id = getMessageArchiveID(original_stanza); this.__super__.getMessageAttributesFromStanza.apply(this, arguments)
if (archive_id) { .then((attrs) => {
attrs.archive_id = archive_id; const archive_id = getMessageArchiveID(original_stanza);
} if (archive_id) {
return attrs; attrs.archive_id = archive_id;
}
resolve(attrs);
}).catch(reject);
});
} }
}, },
......
This diff is collapsed.
...@@ -863,8 +863,8 @@ ...@@ -863,8 +863,8 @@
}; };
u.arrayBufferToString = function (ab) { u.arrayBufferToString = function (ab) {
var enc = new TextDecoder("utf-8"); const enc = new TextDecoder("utf-8");
return enc.decode(new Uint8Array(ab)); return enc.decode(ab);
}; };
u.arrayBufferToBase64 = function (ab) { u.arrayBufferToBase64 = function (ab) {
...@@ -872,6 +872,11 @@ ...@@ -872,6 +872,11 @@
.reduce((data, byte) => data + String.fromCharCode(byte), '')); .reduce((data, byte) => data + String.fromCharCode(byte), ''));
}; };
u.stringToArrayBuffer = function (string) {
const enc = new TextEncoder(); // always utf-8
return enc.encode(string);
};
u.base64ToArrayBuffer = function (b64) { u.base64ToArrayBuffer = function (b64) {
const binary_string = window.atob(b64), const binary_string = window.atob(b64),
len = binary_string.length, len = binary_string.length,
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
var Strophe = converse.env.Strophe; var Strophe = converse.env.Strophe;
var moment = converse.env.moment; var moment = converse.env.moment;
var $iq = converse.env.$iq; var $iq = converse.env.$iq;
var u = converse.env.utils;
window.libsignal = { window.libsignal = {
'SignalProtocolAddress': function (name, device_id) { 'SignalProtocolAddress': function (name, device_id) {
...@@ -20,6 +21,9 @@ ...@@ -20,6 +21,9 @@
'body': 'c1ph3R73X7', 'body': 'c1ph3R73X7',
'registrationId': '1337' 'registrationId': '1337'
}); });
this.decryptWhisperMessage = (key_and_tag) => {
return Promise.resolve(u.stringToArrayBuffer(key_and_tag));
}
}, },
'SessionBuilder': function (storage, remote_address) { 'SessionBuilder': function (storage, remote_address) {
this.processPreKey = function () { this.processPreKey = function () {
......
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