From 251ca2671a409e0937ba42ab0f92f7b1534d6b90 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sun, 26 May 2024 01:30:03 +1000 Subject: [PATCH] Add utility to decrypt a given file --- decrypt_file.py | 52 ++++++++++++++++++++++++++++ lib_cryptomator_utils/cryptomator.py | 32 +++++++++++++++++ list_directory.py | 29 +++------------- 3 files changed, 88 insertions(+), 25 deletions(-) create mode 100755 decrypt_file.py diff --git a/decrypt_file.py b/decrypt_file.py new file mode 100755 index 0000000..922c85e --- /dev/null +++ b/decrypt_file.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 + +# cryptomator-utils: Python utilities for inspecting Cryptomator drives +# Copyright (C) 2024 Lee Yingtong Li (RunasSudo) +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +from lib_cryptomator_utils.cryptomator import decrypt_file, directory_path_to_id, encrypt_filename, hash_directory_id, load_vault_config + +import os +import sys + +def main(): + if len(sys.argv) < 3: + print('Usage: {} /path/to/vault.cryptomator /plaintext/path/within/drive'.format(sys.argv[0]), file=sys.stderr) + sys.exit(1) + + # Parse CLI arguments + vault_config_path = sys.argv[1] + target_directory = sys.argv[2] + + vault_path = os.path.split(vault_config_path)[0] + + # Load vault config (asks for password) + primary_master_key, hmac_master_key = load_vault_config(vault_config_path) + + # Resolve the parent directory of the file + target_directory_parts = target_directory.strip('/').split('/') + directory_id = directory_path_to_id(vault_path, primary_master_key, hmac_master_key, '/'.join(target_directory_parts[:-1])) + + # Decrypt the file + hashed_directory_id = hash_directory_id(primary_master_key, hmac_master_key, directory_id) + encrypted_filename = encrypt_filename(primary_master_key, hmac_master_key, directory_id, target_directory_parts[-1]) + content = decrypt_file(vault_path, primary_master_key, hashed_directory_id, encrypted_filename) + + # Print to stdout + sys.stdout.buffer.write(content) + sys.stdout.buffer.flush() + +if __name__ == '__main__': + main() diff --git a/lib_cryptomator_utils/cryptomator.py b/lib_cryptomator_utils/cryptomator.py index f808e2d..6db5e49 100644 --- a/lib_cryptomator_utils/cryptomator.py +++ b/lib_cryptomator_utils/cryptomator.py @@ -130,6 +130,38 @@ def hash_directory_id(primary_master_key, hmac_master_key, directory_id): hashed_directory_id = base64.b32encode(hashlib.sha1(encrypted_directory_id).digest()).decode('utf-8') return hashed_directory_id +def directory_path_to_id(vault_path, primary_master_key, hmac_master_key, directory_path): + """ + Recurse the drive to resolve the directory ID for the directory at the given plaintext path + """ + + directory_path_parts = directory_path.strip('/').split('/') + + # Begin in root directory + # The root directory in Cryptomator has an empty directory ID + directory_id = '' + + # Traverse path + for path_part in directory_path_parts: + if not path_part: + continue + + # Hash the current directory ID + hashed_directory_id = hash_directory_id(primary_master_key, hmac_master_key, directory_id) + + # Look up the encrypted path_part in the current directory + encrypted_filename = encrypt_filename(primary_master_key, hmac_master_key, directory_id, path_part) + + # Get the directory ID of the part_path + subdirectory_dir_file = os.path.join(vault_path, 'd', hashed_directory_id[:2], hashed_directory_id[2:], encrypted_filename, 'dir.c9r') + with open(subdirectory_dir_file, 'r') as f: + new_directory_id = f.read() + + # Traverse to the new directory ID + directory_id = new_directory_id + + return directory_id + def list_directory(vault_path, primary_master_key, hmac_master_key, directory_id): """ Return a list of files and directories in the directory with the given plaintext directory ID diff --git a/list_directory.py b/list_directory.py index 74aeb0f..e0f895d 100755 --- a/list_directory.py +++ b/list_directory.py @@ -16,7 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -from lib_cryptomator_utils.cryptomator import encrypt_filename, hash_directory_id, list_directory, load_vault_config +from lib_cryptomator_utils.cryptomator import directory_path_to_id, encrypt_filename, hash_directory_id, list_directory, load_vault_config import os import sys @@ -35,32 +35,11 @@ def main(): # Load vault config (asks for password) primary_master_key, hmac_master_key = load_vault_config(vault_config_path) + # Resolve the target directory target_directory_parts = target_directory.strip('/').split('/') + directory_id = directory_path_to_id(vault_path, primary_master_key, hmac_master_key, '/'.join(target_directory_parts)) - # Begin in root directory - # The root directory in Cryptomator has an empty directory ID - directory_id = '' - - # Traverse path - for path_part in target_directory_parts: - if not path_part: - continue - - # Hash the current directory ID - hashed_directory_id = hash_directory_id(primary_master_key, hmac_master_key, directory_id) - - # Look up the encrypted path_part in the current directory - encrypted_filename = encrypt_filename(primary_master_key, hmac_master_key, directory_id, path_part) - - # Get the directory ID of the part_path - subdirectory_dir_file = os.path.join(vault_path, 'd', hashed_directory_id[:2], hashed_directory_id[2:], encrypted_filename, 'dir.c9r') - with open(subdirectory_dir_file, 'r') as f: - new_directory_id = f.read() - - # Traverse to the new directory ID - directory_id = new_directory_id - - # Now we have reached the requested directory, so print directory listing + # Print directory listing for filename in sorted(list_directory(vault_path, primary_master_key, hmac_master_key, directory_id)): print(filename)