From b0779c5b07e74f22c84a497a7927e4a551e14b15 Mon Sep 17 00:00:00 2001 From: wangchuanli Date: Wed, 2 Jul 2025 16:22:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=96=87=E4=BB=B6=E5=8A=A0?= =?UTF-8?q?=E5=AF=86=E5=92=8C=E8=A7=A3=E5=AF=86=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8E=8B=E7=BC=A9=E5=92=8C=E5=88=86=E5=8D=B7?= =?UTF-8?q?=E5=AD=98=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + data/photoprism.png | Bin 0 -> 5885 bytes main.py | 142 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 .gitignore create mode 100644 data/photoprism.png create mode 100644 main.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1d17dae --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.venv diff --git a/data/photoprism.png b/data/photoprism.png new file mode 100644 index 0000000000000000000000000000000000000000..a33d9133409c3a7c6f2d5e1119b1d4dd04ef1bba GIT binary patch literal 5885 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 volume_size: + start = 0 + part_number = 1 + while start < len(encrypted_data): + end = start + volume_size + part_data = encrypted_data[start:end] + part_file_path = os.path.join(encrypted_dir, f"{file_name}.part{part_number}") + with open(part_file_path, 'wb') as part_file: + if part_number == 1: + part_file.write(salt) + part_file.write(part_data) + start = end + part_number += 1 + else: + # 如果数据不需要分卷,直接保存 + output_path = os.path.join(encrypted_dir, f"{file_name}.enc") + with open(output_path, 'wb') as enc_file: + enc_file.write(salt + encrypted_data) + + print(f"Encrypted and saved: {file_path} to {encrypted_dir}") + +# 解密并解压单个文件或分卷 +def decrypt_file(encrypted_dir, password, output_dir): + # 读取所有分卷或单个加密文件 + parts = [os.path.join(encrypted_dir, f) for f in os.listdir(encrypted_dir) if f.rsplit('.')[-1].startswith("part") or f.endswith(".enc")] + parts = sorted(parts) # 确保按顺序读取 + print(f"Parts found: {parts}") + encrypted_data = b"" + salt = None + for i, part in enumerate(parts): + with open(part, 'rb') as part_file: + if i == 0: + salt = part_file.read(16) + encrypted_data += part_file.read() + + if salt is None: + raise ValueError("Salt not found in the first part.") + + # 解密数据 + key = derive_key(password, salt) + decrypted_data = decrypt_data(key, encrypted_data) + + # 解压数据 + decompressed_data = decompress_data(decrypted_data) + output_file = os.path.join(output_dir, os.path.basename(encrypted_dir)) + with open(output_file, 'wb') as f: + f.write(decompressed_data) + + print(f"Decrypted and extracted to: {output_file}") + +# 加密文件夹中的所有文件 +def encrypt_directory(directory, password, volume_size, output_dir): + for root, _, files in os.walk(directory): + for file in files: + file_path = os.path.join(root, file) + encrypt_file(file_path, password, volume_size, output_dir) + +# 解密文件夹中的所有加密文件 +def decrypt_directory(directory, password, output_dir): + for root, dirs, _ in os.walk(directory): + for dir in dirs: + encrypted_dir = os.path.join(root, dir) + decrypt_file(encrypted_dir, password, output_dir) + +if __name__ == "__main__": + action = "encrypt" # 或 "decrypt" + # action = "decrypt" # 或 "decrypt" + password = "123456" + volume_size = 10 * 1024 * 1024 # 每个分卷大小(10 MB) + source_dir = "./data" # 原始数据文件夹 + encrypted_dir = "./encrypted" # 加密文件夹 + decrypted_dir = "./decrypted" # 解密文件夹 + + if action == "encrypt": + encrypt_directory(source_dir, password, volume_size, encrypted_dir) + elif action == "decrypt": + decrypt_directory(encrypted_dir, password, decrypted_dir) + else: + print("Invalid action. Use 'encrypt' or 'decrypt'.") \ No newline at end of file