#!/usr/bin/env python3 # # conversations_http_downloader - download files uploaded by Conversations # # Copyright (C) 2016 Antonio Ospite # # 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 . 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 " % name) def main(): if len(sys.argv) != 2: usage(sys.argv[0]) return 1 url = sys.argv[1] if url.startswith("aesgcm://"): url = "https://" + url[len("aesgcm://"):] 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())