Skip to content

Commit 58d40fe

Browse files
author
爱音爱素食
committed
加密/解密软件基本完成
1 parent 60b90d4 commit 58d40fe

File tree

4 files changed

+86
-85
lines changed

4 files changed

+86
-85
lines changed

.idea/workspace.xml

Lines changed: 24 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crypto_manager.py

Lines changed: 2 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -117,63 +117,8 @@ def encrypt_file(self, input_path: str, output_path: str, signature: str, algo_n
117117

118118
file_size = os.path.getsize(input_path)
119119
processed_size = 0
120-
121-
# with open(input_path, 'rb') as f_in, open(output_path, 'wb') as f_out:
122-
# # Write header: Salt + IV
123-
# f_out.write(salt)
124-
# f_out.write(iv)
125-
#
126-
# while True:
127-
# chunk = f_in.read(self.CHUNK_SIZE)
128-
# if len(chunk) == 0:
129-
# break
130-
#
131-
# processed_size += len(chunk)
132-
#
133-
# if len(chunk) < self.CHUNK_SIZE:
134-
# # Last chunk, needs padding
135-
# padded_chunk = self._pad_data(chunk)
136-
# f_out.write(encryptor.update(padded_chunk) + encryptor.finalize())
137-
# else:
138-
# # Full chunk.
139-
# # Note: Padding PKCS7 on stream is tricky.
140-
# # Usually we treat the whole file as one message.
141-
# # Standard approach for streams: Pad only the last block.
142-
# # Cryptography's padder handles this if we feed it right,
143-
# # but we need to know if it's the last chunk.
144-
# # The 'padder' object is stateful? No, padder is for one-shot or update().
145-
# # Actually, `padding.PKCS7` padder doesn't support stream updating easily if we don't know the end.
146-
# # BUT, we do know the end (len(chunk) < CHUNK_SIZE or next read is empty).
147-
# # Let's use a simpler approach: Read all? No, memory issues.
148-
# # Correct streaming padding:
149-
# # 1. Read chunk.
150-
# # 2. If it's the last chunk (EOF), pad it and encrypt.
151-
# # 3. If not last, encrypt raw.
152-
# # Wait, AES requires blocks of 16 bytes. If chunk is 64KB, it's a multiple of 16.
153-
# # So we can encrypt full chunks directly.
154-
# # Only the last chunk needs padding.
155-
#
156-
# # Logic revision:
157-
# # AES CBC works on blocks. 64KB is multiple of 16.
158-
# # We can encrypt valid blocks directly.
159-
# # We only need the padder for the *final* bytes.
160-
# # BUT, PKCS7 always adds padding, even if multiple of 16 (adds a full block of 16s).
161-
# # So we can effectively say:
162-
# # - Loop read.
163-
# # - If next read is empty, this was the last chunk.
164-
# # - But we don't know if next read is empty until we try.
165-
# # Buffered approach:
166-
# pass
167-
#
168-
# if progress_callback:
169-
# progress_callback(processed_size, file_size)
170-
171-
# Re-implementing file loop for correct padding
172-
# We need to ensure we pad the final block.
173-
# And we must ensure previous blocks are multiples of 16.
174-
175-
# Reset
176-
encryptor = cipher.encryptor() # Fresh encryptor
120+
121+
encryptor = cipher.encryptor()
177122
padder = padding.PKCS7(128).padder()
178123

179124
with open(input_path, 'rb') as f_in, open(output_path, 'wb') as f_out:
@@ -183,10 +128,6 @@ def encrypt_file(self, input_path: str, output_path: str, signature: str, algo_n
183128
while True:
184129
chunk = f_in.read(self.CHUNK_SIZE)
185130
if len(chunk) == 0:
186-
# End of file. Finalize padding.
187-
# The padder might have pending data? No, we feed chunk by chunk.
188-
# If we feed chunk to padder.update(), it buffers.
189-
# Let's do this:
190131
final_data = padder.finalize()
191132
f_out.write(encryptor.update(final_data) + encryptor.finalize())
192133
break
@@ -233,8 +174,6 @@ def decrypt_file(self, input_path: str, output_path: str, signature: str, algo_n
233174
# End of stream
234175
final_unpadded = unpadder.finalize()
235176
f_out.write(final_unpadded)
236-
# Decryptor finalize usually returns empty for CBC if aligned?
237-
# Actually decryptor.finalize() checks for leftover bytes which shouldn't exist in CBC if padded correctly.
238177
decryptor.finalize()
239178
break
240179

main.py

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import threading
44
import os
55
import datetime
6+
import json
67
from crypto_manager import CryptoManager
78

89
# Try to import tkinterdnd2 for Drag and Drop support
@@ -54,6 +55,8 @@
5455
"lang_label": {"en": "Language:", "zh": "语言:"}
5556
}
5657

58+
CONFIG_FILE = "settings.json"
59+
5760
class EncryptApp(BaseWindow):
5861
def __init__(self):
5962
super().__init__()
@@ -65,10 +68,38 @@ def __init__(self):
6568

6669
self.lang_code = "en"
6770
self.translatable_widgets = [] # List of (widget, key, attribute_name)
71+
72+
# Load settings before UI setup to apply language and paths
73+
self.load_settings()
6874

6975
self.setup_ui()
70-
self.update_language() # Initial text set
76+
self.update_language() # Apply language to UI
7177

78+
def load_settings(self):
79+
"""Load settings from JSON file."""
80+
if os.path.exists(CONFIG_FILE):
81+
try:
82+
with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
83+
settings = json.load(f)
84+
self.enc_output_path.set(settings.get("enc_output_path", ""))
85+
self.dec_output_path.set(settings.get("dec_output_path", ""))
86+
self.lang_code = settings.get("language", "en")
87+
except Exception as e:
88+
print(f"Failed to load settings: {e}")
89+
90+
def save_settings(self):
91+
"""Save current settings to JSON file."""
92+
settings = {
93+
"enc_output_path": self.enc_output_path.get(),
94+
"dec_output_path": self.dec_output_path.get(),
95+
"language": self.lang_code
96+
}
97+
try:
98+
with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
99+
json.dump(settings, f, ensure_ascii=False, indent=4)
100+
except Exception as e:
101+
print(f"Failed to save settings: {e}")
102+
72103
def tr(self, key):
73104
"""Translate a key to the current language."""
74105
return TRANSLATIONS.get(key, {}).get(self.lang_code, key)
@@ -95,6 +126,9 @@ def update_language(self, event=None):
95126
self.lang_code = "zh"
96127
else:
97128
self.lang_code = "en"
129+
130+
# Save new language preference
131+
self.save_settings()
98132

99133
# Update registered widgets
100134
for widget, key, attr in self.translatable_widgets:
@@ -108,9 +142,6 @@ def update_language(self, event=None):
108142
print(f"Failed to update widget {widget}: {e}")
109143

110144
# Update dynamic status labels if they are "Ready"
111-
# (This is a bit tricky, but we can reset them to Ready on lang change if they were Ready)
112-
# Simplified: We just leave them as is, next operation will use new lang.
113-
# But we can force update the "Ready" state if idle.
114145
if self.enc_status.cget("text") in ["Ready", "就绪"]:
115146
self.enc_status.config(text=self.tr("ready"))
116147
if self.dec_status.cget("text") in ["Ready", "就绪"]:
@@ -154,7 +185,12 @@ def setup_ui(self):
154185
lbl_lang.pack(side="left", padx=5)
155186

156187
self.lang_combo = ttk.Combobox(top_frame, values=["English", "中文 (Chinese)"], state="readonly", width=15)
157-
self.lang_combo.set("English")
188+
# Set initial selection based on loaded settings
189+
if self.lang_code == "zh":
190+
self.lang_combo.set("中文 (Chinese)")
191+
else:
192+
self.lang_combo.set("English")
193+
158194
self.lang_combo.pack(side="left", padx=5)
159195
self.lang_combo.bind("<<ComboboxSelected>>", self.update_language)
160196

@@ -203,14 +239,12 @@ def create_text_section(self, parent, col, title_key, btn_text_key, btn_command)
203239
log_area = scrolledtext.ScrolledText(frame, height=15, state="disabled", font=("Consolas", 9))
204240
log_area.pack(fill="both", expand=True, pady=(0, 5))
205241

206-
if "Encrypt" in self.tr(btn_text_key) or "加密" in self.tr(btn_text_key): # Check intent via key ideally, but simplistic here
207-
# Better to use the key passed
242+
if "Encrypt" in self.tr(btn_text_key) or "加密" in self.tr(btn_text_key):
208243
if btn_text_key == "btn_encrypt":
209244
self.enc_log = log_area
210245
else:
211246
self.dec_log = log_area
212247
else:
213-
# Fallback if I messed up logic above, rely on passed key
214248
if btn_text_key == "btn_encrypt":
215249
self.enc_log = log_area
216250
else:
@@ -321,11 +355,15 @@ def confirm_signature(self):
321355

322356
def select_enc_out_dir(self):
323357
d = filedialog.askdirectory()
324-
if d: self.enc_output_path.set(d)
358+
if d:
359+
self.enc_output_path.set(d)
360+
self.save_settings()
325361

326362
def select_dec_out_dir(self):
327363
d = filedialog.askdirectory()
328-
if d: self.dec_output_path.set(d)
364+
if d:
365+
self.dec_output_path.set(d)
366+
self.save_settings()
329367

330368
def select_file(self, var):
331369
f = filedialog.askopenfilename()
@@ -378,6 +416,14 @@ def _run_file_op(self, input_var, output_dir_var, op_func, pb, status_lbl, suffi
378416
return
379417

380418
out_dir = output_dir_var.get()
419+
if out_dir:
420+
if not os.path.exists(out_dir):
421+
try:
422+
os.makedirs(out_dir)
423+
except OSError:
424+
# If cannot create, fallback to input directory
425+
out_dir = ""
426+
381427
if not out_dir:
382428
out_dir = os.path.dirname(in_path)
383429

requirements.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,4 @@
1-
cryptography>=41.0.0
2-
tkinterdnd2>=0.3.0
1+
cffi==2.0.0
2+
cryptography==41.0.0
3+
pycparser==2.23
4+
tkinterdnd2==0.3.0

0 commit comments

Comments
 (0)