From 30369495de972b5692fa33a271af9f259f42f374 Mon Sep 17 00:00:00 2001 From: Sean Kelley Date: Sat, 26 Jan 2019 15:49:29 -0800 Subject: [PATCH 1/3] Skip file whose header is unparseable (or empty). --- .gitignore | 2 ++ Synology.py | 16 ++++++++-------- syndecrypt/__main__.py | 15 +++++++-------- syndecrypt/core.py | 8 +++++++- 4 files changed, 24 insertions(+), 17 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e0e8c99 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +.DS_Store diff --git a/Synology.py b/Synology.py index 98cadf6..c5144bd 100644 --- a/Synology.py +++ b/Synology.py @@ -83,7 +83,7 @@ def start(): def load_file(): fname = tkinter.filedialog.askopenfilename() - + if fname: try: pkfilebox.config(state='normal') @@ -105,7 +105,7 @@ def load_file(): def load_filepu(): fname = tkinter.filedialog.askopenfilename() - + if fname: try: pufilebox.config(state='normal') @@ -142,7 +142,7 @@ def load_decrypt_file(): fname = tkinter.filedialog.askopenfilename() else: fname = tkinter.filedialog.askdirectory() - + if fname: try: filebox.config(state='normal') @@ -164,7 +164,7 @@ def load_decrypt_file(): def load_output(): fname = tkinter.filedialog.askdirectory() - + if fname: try: outputbox.config(state='normal') @@ -198,7 +198,7 @@ def validate(): try: run_tool() except Exception as e: - elogger.error(e) + elogger.error(e, exc_info=e) tkinter.messagebox.showwarning("Decryption", "Failed to decrypt file(s), please raise an issue using the support button located in the about option of the application menu") else: tkinter.messagebox.showinfo("Decryption", "Files have successfully been decrypted and can be found in the destination folder") @@ -226,7 +226,7 @@ def validate(): try: run_tool() except Exception as e: - elogger.error(e) + elogger.error(e, exc_info=e) tkinter.messagebox.showwarning("Decryption", "Failed to decrypt file(s), please raise an issue using the support button located in the about option of the application menu") else: tkinter.messagebox.showinfo("Decryption", "Files have successfully been decrypted and can be found in the destination folder") @@ -317,7 +317,7 @@ def about_dialog(): win.title("About") win.configure(bg="#e7e7e7") win.resizable(0,0) - + img = ImageTk.PhotoImage(Image.open("app.gif").resize((128, 128), Image.ANTIALIAS)) icon = ttk.Label(win, image=img) icon.image = img @@ -331,7 +331,7 @@ def about_dialog(): license.config(state='disabled', highlightbackground='grey', highlightcolor='grey', borderwidth=1, highlightthickness=1) support = ttk.Button(win, text="Support", command=lambda: open_url("https://github.com/anojht/synology-cloud-sync-decrypt-tool/issues")) donate = ttk.Button(win, text="Donate", command=lambda: open_url("https://www.paypal.me/Anojh")) - + icon.grid(row=0, column=1, padx=10) name.grid(row=1, column=1, padx=10) author.grid(row=2, column=1) diff --git a/syndecrypt/__main__.py b/syndecrypt/__main__.py index 9d7f421..fb3974f 100644 --- a/syndecrypt/__main__.py +++ b/syndecrypt/__main__.py @@ -23,19 +23,20 @@ import docopt import os import logging - +import sys + import syndecrypt.files as files #import files import syndecrypt.util as util #from syndecrypt import util def main(args): - + if args[0] == "-p": arguments = {"--password-file": args[1], "--private-key-file": None, "--public-key-file": None, "--output-directory": args[2], "": args[3]} elif args[0] == "-k": arguments = {"--password-file": None, "--private-key-file": args[1], "--public-key-file": args[2], "--output-directory": args[3], "": args[4]} - + password_file_name = arguments['--password-file'] if password_file_name != None: password = arguments['--password-file'] @@ -60,7 +61,7 @@ def main(args): f = arguments[''] ff = os.path.abspath(f) fp = os.path.basename(ff) - + if os.path.isdir(ff): if not os.path.isdir(os.path.join(output_dir, fp)): output_dir = os.path.join(output_dir, fp) @@ -71,11 +72,9 @@ def main(args): structure = root.replace(ff, output_dir, 1) if not os.path.isdir(structure): os.mkdir(structure) - + for filename in items: - file_path = os.path.join(root, filename) - if filename != ".DS_Store": - files.decrypt_file(file_path, os.path.join(structure, filename), password=password, private_key=private_key, public_key=public_key) + files.decrypt_file(os.path.join(root, filename), os.path.join(structure, filename), password=password, private_key=private_key, public_key=public_key) else: files.decrypt_file(ff, os.path.join(output_dir, fp), password=password, private_key=private_key, public_key=public_key) diff --git a/syndecrypt/core.py b/syndecrypt/core.py index 12c3938..ad06b6e 100644 --- a/syndecrypt/core.py +++ b/syndecrypt/core.py @@ -31,7 +31,7 @@ # pwd and salt must be bytes objects def _openssl_kdf(algo, pwd, salt, key_size, iv_size, iteration): temp = b'' - + fd = temp while len(fd) < key_size + iv_size: try: @@ -173,14 +173,17 @@ def read_header(f): s = f.read(len(MAGIC)) if s != MAGIC: LOGGER.error('magic should not be ' + str(s) + ' but ' + str(MAGIC)) + return None s = f.read(32) magic_hash = hashlib.md5(MAGIC).hexdigest().encode('ascii') if s != magic_hash: LOGGER.error('magic hash should not be ' + str(s) + ' but ' + str(magic_hash)) + return None header = _read_object_from(f) if header['type'] != 'metadata': LOGGER.error('first object must have "metadata" type but found ' + header['type']) + return None return header def read_chunks(f): @@ -203,6 +206,9 @@ def outstream_writer_and_md5_digestor(decompressed_chunk): # create session key and decryptor header = read_header(instream) + if not header: + LOGGER.info('failed to parse header; skipping file...') + return # TODO: assert version and hash algo decrypt_stream.md5_digestor = hashlib.md5() if password != None: From aa1bfdc90c447da466624ff6574e5e6cd1733169 Mon Sep 17 00:00:00 2001 From: Sean Kelley Date: Sat, 26 Jan 2019 16:16:34 -0800 Subject: [PATCH 2/3] Use all available cores when decrypting directories. --- syndecrypt/__main__.py | 25 +++++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/syndecrypt/__main__.py b/syndecrypt/__main__.py index fb3974f..86aff77 100644 --- a/syndecrypt/__main__.py +++ b/syndecrypt/__main__.py @@ -24,6 +24,7 @@ import os import logging import sys +from multiprocessing import Pool import syndecrypt.files as files #import files @@ -68,13 +69,29 @@ def main(args): os.mkdir(output_dir) else: print("Folder already exists!") - for root, subdirs, items in os.walk(ff): - structure = root.replace(ff, output_dir, 1) + + directories = list(os.walk(ff)) + + for input_dir, _, _ in directories: + structure = input_dir.replace(ff, output_dir, 1) if not os.path.isdir(structure): os.mkdir(structure) - for filename in items: - files.decrypt_file(os.path.join(root, filename), os.path.join(structure, filename), password=password, private_key=private_key, public_key=public_key) + decrypt_args = [] + + for input_dir, _, filenames in directories: + for filename in filenames: + decrypt_args.append(( + os.path.join(input_dir, filename), + os.path.join(input_dir.replace(ff, output_dir, 1), filename), + password, + private_key, + public_key, + )) + + with Pool() as p: + p.starmap(files.decrypt_file, decrypt_args) + else: files.decrypt_file(ff, os.path.join(output_dir, fp), password=password, private_key=private_key, public_key=public_key) From 35a3322ad4200ccba1987ac0c6aabad46379045d Mon Sep 17 00:00:00 2001 From: Sean Kelley Date: Sat, 26 Jan 2019 22:03:50 -0800 Subject: [PATCH 3/3] Remove .gitignore per PR comments. --- .gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 .gitignore diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e0e8c99..0000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -__pycache__ -.DS_Store