Description: make the OMEMO plugin compliant with XEP-0384
Author: Philipp Hörist (upstream code), W. Martin Borgert (Debian patch)
Origin: upstream
Bug-Debian: #859894
Applied-Upstream: 1b8c8a5a, 22271eb1, 81caa982
Last-Update: 2017-04-09
---
This patch header follows DEP-3: http://dep.debian.net/deps/dep3/
--- a/omemo/aes_gcm_fallback.py
+++ b/omemo/aes_gcm_fallback.py
@@ -140,13 +140,18 @@
 
 def aes_encrypt(key, nonce, plaintext):
     """ Use AES128 GCM with the given key and iv to encrypt the payload. """
-    c, t = gcm_encrypt(key, nonce, plaintext, '')
-    result = c + t
-    return result
+    return gcm_encrypt(key, nonce, plaintext, '')
 
-
-def aes_decrypt(key, nonce, payload):
+def aes_decrypt(_key, nonce, payload):
     """ Use AES128 GCM with the given key and iv to decrypt the payload. """
-    ciphertext = payload[:-16]
-    mac = payload[-16:]
+    if len(_key) >= 32:
+        # XEP-0384
+        ciphertext = payload
+        key = _key[:16]
+        mac = _key[16:]
+    else:
+        # Legacy
+        ciphertext = payload[:-16]
+        key = _key
+        mac = payload[-16:]
     return gcm_decrypt(key, nonce, ciphertext, '', mac)
--- a/omemo/aes_gcm_native.py
+++ b/omemo/aes_gcm_native.py
@@ -33,10 +33,18 @@
     from cryptography.hazmat.backends import default_backend
 
 
-def aes_decrypt(key, iv, payload):
+def aes_decrypt(_key, iv, payload):
     """ Use AES128 GCM with the given key and iv to decrypt the payload. """
-    data = payload[:-16]
-    tag = payload[-16:]
+    if len(_key) >= 32:
+        # XEP-0384
+        data = payload
+        key = _key[:16]
+        tag = _key[16:]
+    else:
+        # Legacy
+        data = payload[:-16]
+        key = _key
+        tag = payload[-16:]
     if os.name == 'nt':
         _backend = backend
     else:
@@ -58,4 +66,4 @@
         algorithms.AES(key),
         GCM(iv),
         backend=_backend).encryptor()
-    return encryptor.update(plaintext) + encryptor.finalize() + encryptor.tag
+    return encryptor.update(plaintext) + encryptor.finalize(), encryptor.tag
--- a/omemo/state.py
+++ b/omemo/state.py
@@ -226,12 +226,18 @@
             log.error('No known devices')
             return
 
+        payload, tag = encrypt(key, iv, plaintext)
+
+        key += tag
+
         # Encrypt the message key with for each of receivers devices
         for device in devices_list:
             try:
                 if self.isTrusted(jid, device) == TRUSTED:
                     cipher = self.get_session_cipher(jid, device)
-                    encrypted_keys[device] = cipher.encrypt(key).serialize()
+                    cipher_key = cipher.encrypt(key)
+                    prekey = isinstance(cipher_key, PreKeyWhisperMessage)
+                    encrypted_keys[device] = (cipher_key.serialize(), prekey)
                 else:
                     log.debug('Skipped Device because Trust is: ' +
                               str(self.isTrusted(jid, device)))
@@ -248,15 +254,15 @@
             try:
                 if self.isTrusted(from_jid, device) == TRUSTED:
                     cipher = self.get_session_cipher(from_jid, device)
-                    encrypted_keys[device] = cipher.encrypt(key).serialize()
+                    cipher_key = cipher.encrypt(key)
+                    prekey = isinstance(cipher_key, PreKeyWhisperMessage)
+                    encrypted_keys[device] = (cipher_key.serialize(), prekey)
                 else:
                     log.debug('Skipped own Device because Trust is: ' +
                               str(self.isTrusted(from_jid, device)))
             except:
                 log.warning('Failed to find key for device ' + str(device))
 
-        payload = encrypt(key, iv, plaintext)
-
         result = {'sid': self.own_device_id,
                   'keys': encrypted_keys,
                   'jid': jid,
@@ -279,6 +285,10 @@
             log.error('No known devices')
             return
 
+        payload, tag = encrypt(key, iv, plaintext)
+
+        key += tag
+
         for tup in devices_list:
             self.get_session_cipher(tup[0], tup[1])
 
@@ -292,8 +302,9 @@
             for rid, cipher in self.session_ciphers[jid_to].items():
                 try:
                     if self.isTrusted(jid_to, rid) == TRUSTED:
-                        encrypted_keys[rid] = cipher.encrypt(key). \
-                            serialize()
+                        cipher_key = cipher.encrypt(key)
+                        prekey = isinstance(cipher_key, PreKeyWhisperMessage)
+                        encrypted_keys[rid] = (cipher_key.serialize(), prekey)
                     else:
                         log.debug('Skipped Device because Trust is: ' +
                                   str(self.isTrusted(jid_to, rid)))
@@ -313,7 +324,9 @@
             try:
                 cipher = self.get_session_cipher(from_jid, dev)
                 if self.isTrusted(from_jid, dev) == TRUSTED:
-                    encrypted_keys[dev] = cipher.encrypt(key).serialize()
+                    cipher_key = cipher.encrypt(key)
+                    prekey = isinstance(cipher_key, PreKeyWhisperMessage)
+                    encrypted_keys[dev] = (cipher_key.serialize(), prekey)
                 else:
                     log.debug('Skipped own Device because Trust is: ' +
                               str(self.isTrusted(from_jid, dev)))
@@ -321,8 +334,6 @@
                 log.exception('ERROR:')
                 log.warning('Failed to find key for device ' + str(dev))
 
-        payload = encrypt(key, iv, plaintext)
-
         result = {'sid': self.own_device_id,
                   'keys': encrypted_keys,
                   'jid': jid,
--- a/xmpp.py
+++ b/xmpp.py
@@ -79,9 +79,14 @@
         # , contact_jid, key, iv, payload, dev_id, my_dev_id):
         Node.__init__(self, 'encrypted', attrs={'xmlns': NS_OMEMO})
         header = Node('header', attrs={'sid': msg_dict['sid']})
-        for rid, key in msg_dict['keys'].items():
-            header.addChild('key', attrs={'rid': rid}).addData(b64encode(key))
-
+        for rid, (key, prekey) in msg_dict['keys'].items():
+            if prekey:
+                child = header.addChild('key',
+                                        attrs={'prekey': 'true', 'rid': rid})
+            else:
+                child = header.addChild('key',
+                                        attrs={'rid': rid})
+            child.addData(b64encode(key))
         header.addChild('iv').addData(b64encode(msg_dict['iv']))
         self.addChild(node=header)
         self.addChild('payload').addData(b64encode(msg_dict['payload']))
