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

OMEMO: fix timing errors

- Use async/await
- Wait for devices to be created before continuing
parent f78837cb
...@@ -6,31 +6,29 @@ ...@@ -6,31 +6,29 @@
async function deviceListFetched (_converse, jid) { async function deviceListFetched (_converse, jid) {
const stanza = await u.waitUntil(() => _.filter( const selector = `iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.devicelist"]`;
_converse.connection.IQ_stanzas, const stanza = await u.waitUntil(
iq => iq.querySelector(`iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.devicelist"]`) () => Array.from(_converse.connection.IQ_stanzas).filter(iq => iq.querySelector(selector)).pop()
).pop()); );
await u.waitUntil(() => _converse.devicelists.get(jid)); await u.waitUntil(() => _converse.devicelists.get(jid));
return stanza; return stanza;
} }
function ownDeviceHasBeenPublished (_converse) { function ownDeviceHasBeenPublished (_converse) {
return _.filter( return _.filter(
_converse.connection.IQ_stanzas, Array.from(_converse.connection.IQ_stanzas),
iq => iq.querySelector('iq[from="'+_converse.bare_jid+'"] publish[node="eu.siacs.conversations.axolotl.devicelist"]') iq => iq.querySelector('iq[from="'+_converse.bare_jid+'"] publish[node="eu.siacs.conversations.axolotl.devicelist"]')
).pop(); ).pop();
} }
function bundleHasBeenPublished (_converse) { function bundleHasBeenPublished (_converse) {
return _.filter( const selector = 'publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]';
_converse.connection.IQ_stanzas, return Array.from(_converse.connection.IQ_stanzas).filter(iq => iq.querySelector(selector)).pop();
iq => iq.querySelector('publish[node="eu.siacs.conversations.axolotl.bundles:123456789"]')
).pop();
} }
function bundleFetched (_converse, jid, device_id) { function bundleFetched (_converse, jid, device_id) {
return _.filter( return _.filter(
_converse.connection.IQ_stanzas, Array.from(_converse.connection.IQ_stanzas),
iq => iq.querySelector(`iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.bundles:${device_id}"]`) iq => iq.querySelector(`iq[to="${jid}"] items[node="eu.siacs.conversations.axolotl.bundles:${device_id}"]`)
).pop(); ).pop();
} }
......
...@@ -294,13 +294,13 @@ converse.plugins.add('converse-omemo', { ...@@ -294,13 +294,13 @@ converse.plugins.add('converse-omemo', {
}, },
async decryptMessage (obj) { async decryptMessage (obj) {
const key_obj = await crypto.subtle.importKey('raw', obj.key, KEY_ALGO, true, ['encrypt','decrypt']), const key_obj = await crypto.subtle.importKey('raw', obj.key, KEY_ALGO, true, ['encrypt','decrypt']);
cipher = u.appendArrayBuffer(u.base64ToArrayBuffer(obj.payload), obj.tag), const cipher = u.appendArrayBuffer(u.base64ToArrayBuffer(obj.payload), obj.tag);
algo = { const algo = {
'name': "AES-GCM", 'name': "AES-GCM",
'iv': u.base64ToArrayBuffer(obj.iv), 'iv': u.base64ToArrayBuffer(obj.iv),
'tagLength': TAG_LENGTH 'tagLength': TAG_LENGTH
} };
return u.arrayBufferToString(await crypto.subtle.decrypt(algo, key_obj, cipher)); return u.arrayBufferToString(await crypto.subtle.decrypt(algo, key_obj, cipher));
}, },
...@@ -316,8 +316,9 @@ converse.plugins.add('converse-omemo', { ...@@ -316,8 +316,9 @@ converse.plugins.add('converse-omemo', {
}, },
async handleDecryptedWhisperMessage (attrs, key_and_tag) { async handleDecryptedWhisperMessage (attrs, key_and_tag) {
const encrypted = attrs.encrypted, const encrypted = attrs.encrypted;
devicelist = _converse.devicelists.getDeviceList(this.get('jid')); const devicelist = _converse.devicelists.getDeviceList(this.get('jid'));
await devicelist._devices_promise;
this.save('omemo_supported', true); this.save('omemo_supported', true);
let device = devicelist.get(encrypted.device_id); let device = devicelist.get(encrypted.device_id);
...@@ -325,44 +326,43 @@ converse.plugins.add('converse-omemo', { ...@@ -325,44 +326,43 @@ converse.plugins.add('converse-omemo', {
device = devicelist.devices.create({'id': encrypted.device_id, 'jid': attrs.from}); device = devicelist.devices.create({'id': encrypted.device_id, 'jid': attrs.from});
} }
if (encrypted.payload) { if (encrypted.payload) {
const key = key_and_tag.slice(0, 16), const key = key_and_tag.slice(0, 16);
tag = key_and_tag.slice(16); const tag = key_and_tag.slice(16);
const result = await this.decryptMessage(Object.assign(encrypted, {'key': key, 'tag': tag})); const result = await this.decryptMessage(Object.assign(encrypted, {'key': key, 'tag': tag}));
device.save('active', true); device.save('active', true);
return result; return result;
} }
}, },
decrypt (attrs) { async decrypt (attrs) {
const session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10)); const session_cipher = this.getSessionCipher(attrs.from, parseInt(attrs.encrypted.device_id, 10));
// https://xmpp.org/extensions/xep-0384.html#usecases-receiving // https://xmpp.org/extensions/xep-0384.html#usecases-receiving
const key = u.base64ToArrayBuffer(attrs.encrypted.key);
if (attrs.encrypted.prekey === true) { if (attrs.encrypted.prekey === true) {
let plaintext; try {
return session_cipher.decryptPreKeyWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary') const key_and_tag = await session_cipher.decryptPreKeyWhisperMessage(key, 'binary');
.then(key_and_tag => this.handleDecryptedWhisperMessage(attrs, key_and_tag)) const plaintext = await this.handleDecryptedWhisperMessage(attrs, key_and_tag);
.then(pt => { await _converse.omemo_store.generateMissingPreKeys();
plaintext = pt; await _converse.omemo_store.publishBundle();
return _converse.omemo_store.generateMissingPreKeys();
}).then(() => _converse.omemo_store.publishBundle())
.then(() => {
if (plaintext) { if (plaintext) {
return Object.assign(attrs, {'plaintext': plaintext}); return Object.assign(attrs, {'plaintext': plaintext});
} else { } else {
return Object.assign(attrs, {'is_only_key': true}); return Object.assign(attrs, {'is_only_key': true});
} }
}).catch(e => { } catch (e) {
this.reportDecryptionError(e); this.reportDecryptionError(e);
return attrs; return attrs;
}); }
} else { } else {
return session_cipher.decryptWhisperMessage(u.base64ToArrayBuffer(attrs.encrypted.key), 'binary') try {
.then(key_and_tag => this.handleDecryptedWhisperMessage(attrs, key_and_tag)) const key_and_tag = await session_cipher.decryptWhisperMessage(key, 'binary')
.then(plaintext => Object.assign(attrs, {'plaintext': plaintext})) const plaintext = await this.handleDecryptedWhisperMessage(attrs, key_and_tag);
.catch(e => { return Object.assign(attrs, {'plaintext': plaintext});
} catch (e) {
this.reportDecryptionError(e); this.reportDecryptionError(e);
return attrs; return attrs;
}); }
} }
}, },
...@@ -887,7 +887,12 @@ converse.plugins.add('converse-omemo', { ...@@ -887,7 +887,12 @@ converse.plugins.add('converse-omemo', {
const keys = await Promise.all(_.range(0, _converse.NUM_PREKEYS).map(id => libsignal.KeyHelper.generatePreKey(id))); const keys = await Promise.all(_.range(0, _converse.NUM_PREKEYS).map(id => libsignal.KeyHelper.generatePreKey(id)));
keys.forEach(k => _converse.omemo_store.storePreKey(k.keyId, k.keyPair)); keys.forEach(k => _converse.omemo_store.storePreKey(k.keyId, k.keyPair));
const devicelist = _converse.devicelists.get(_converse.bare_jid); const devicelist = _converse.devicelists.get(_converse.bare_jid);
const device = devicelist.devices.create({'id': bundle.device_id, 'jid': _converse.bare_jid}); const device = await new Promise((success, error) => {
devicelist.devices.create(
{'id': bundle.device_id, 'jid': _converse.bare_jid},
{success, 'error': (m, e) => error(e)}
);
});
const marshalled_keys = keys.map(k => ({'id': k.keyId, 'key': u.arrayBufferToBase64(k.keyPair.pubKey)})); const marshalled_keys = keys.map(k => ({'id': k.keyId, 'key': u.arrayBufferToBase64(k.keyPair.pubKey)}));
bundle['prekeys'] = marshalled_keys; bundle['prekeys'] = marshalled_keys;
device.save('bundle', bundle); device.save('bundle', bundle);
...@@ -981,6 +986,7 @@ converse.plugins.add('converse-omemo', { ...@@ -981,6 +986,7 @@ converse.plugins.add('converse-omemo', {
const id = `converse.devicelist-${_converse.bare_jid}-${this.get('jid')}`; const id = `converse.devicelist-${_converse.bare_jid}-${this.get('jid')}`;
this.devices.browserStorage = _converse.createStore(id); this.devices.browserStorage = _converse.createStore(id);
this.fetchDevices(); this.fetchDevices();
}, },
async onDevicesFound (collection) { async onDevicesFound (collection) {
...@@ -1046,8 +1052,13 @@ converse.plugins.add('converse-omemo', { ...@@ -1046,8 +1052,13 @@ converse.plugins.add('converse-omemo', {
log.error(e); log.error(e);
return []; return [];
} }
const device_ids = sizzle(`list[xmlns="${Strophe.NS.OMEMO}"] device`, iq).map(dev => dev.getAttribute('id')); const selector = `list[xmlns="${Strophe.NS.OMEMO}"] device`;
_.forEach(device_ids, id => this.devices.create({'id': id, 'jid': this.get('jid')})); const device_ids = sizzle(selector, iq).map(d => d.getAttribute('id'));
const deviceFactory = (id, success, error) => {
this.devices.create({id, 'jid': this.get('jid')}, {success, 'error': (m, e) => error(e)})
}
const promises = device_ids.map(id => new Promise((resolve, reject) => deviceFactory(id, resolve, reject)));
await Promise.all(promises);
return device_ids; return device_ids;
}, },
...@@ -1094,10 +1105,12 @@ converse.plugins.add('converse-omemo', { ...@@ -1094,10 +1105,12 @@ converse.plugins.add('converse-omemo', {
async function fetchOwnDevices () { async function fetchOwnDevices () {
await fetchDeviceLists(); await fetchDeviceLists();
let own_devicelist = _converse.devicelists.get(_converse.bare_jid); let own_devicelist = _converse.devicelists.get(_converse.bare_jid);
if (!own_devicelist) { if (own_devicelist) {
own_devicelist.fetchDevices();
} else {
own_devicelist = _converse.devicelists.create({'jid': _converse.bare_jid}); own_devicelist = _converse.devicelists.create({'jid': _converse.bare_jid});
} }
return own_devicelist.fetchDevices(); return own_devicelist._devices_promise;
} }
function updateBundleFromStanza (stanza) { function updateBundleFromStanza (stanza) {
......
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