Răsfoiți Sursa

OMEMO: fix timing errors

- Use async/await
- Wait for devices to be created before continuing
JC Brand 5 ani în urmă
părinte
comite
7d7c41a135
2 a modificat fișierele cu 62 adăugiri și 51 ștergeri
  1. 8 10
      spec/omemo.js
  2. 54 41
      src/converse-omemo.js

+ 8 - 10
spec/omemo.js

@@ -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();
     }

+ 54 - 41
src/converse-omemo.js

@@ -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) {