+#!/usr/bin/env python3
+#
+# conversations_http_downloader - download files uploaded by Conversations
+#
+# Copyright (C) 2016 Antonio Ospite <ao2@ao2.it>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import os
+import sys
+import urllib.error
+import urllib.parse
+import urllib.request
+
+from cryptography.hazmat.backends import default_backend
+from cryptography.hazmat.primitives.ciphers import algorithms
+from cryptography.hazmat.primitives.ciphers import Cipher
+from cryptography.hazmat.primitives.ciphers.modes import GCM
+
+
+# Taken verbatim from python-omemo
+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:]
+ backend = default_backend()
+ decryptor = Cipher(
+ algorithms.AES(key),
+ GCM(iv, tag=tag),
+ backend=backend).decryptor()
+ return decryptor.update(data) + decryptor.finalize()
+
+
+# Inspired by https://github.com/iNPUTmice/ImageDownloader
+def decrypt(data, anchor):
+ """ Use the iv an key that Conversations put into the URL anchor. """
+ key_and_iv = bytes.fromhex(anchor)
+ iv = key_and_iv[0:16]
+ key = key_and_iv[16:48]
+ return aes_decrypt(key, iv, data)
+
+
+def usage(name):
+ print("usage: %s <URL>" % name)
+
+
+def main():
+ if len(sys.argv) != 2:
+ usage(sys.argv[0])
+ return 1
+
+ url = sys.argv[1]
+
+ parsed_url = urllib.parse.urlparse(url)
+ anchor = parsed_url.fragment
+
+ try:
+ response = urllib.request.urlopen(url)
+ data = response.read()
+ except (urllib.error.URLError, urllib.error.HTTPError) as error:
+ sys.stderr.write("Error: %s\n" % error)
+ return 1
+
+ filename = os.path.basename(parsed_url.path)
+ with open(filename, "xb") as output_file:
+ if len(anchor) == 96:
+ output_file.write(decrypt(data, anchor))
+ else:
+ output_file.write(data)
+
+ return 0
+
+
+if __name__ == "__main__":
+ sys.exit(main())