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