Skip to content

Commit 3632acf

Browse files
authored
Update FileCryptor.py
1 parent b18b77d commit 3632acf

1 file changed

Lines changed: 86 additions & 110 deletions

File tree

112-File-Encryption-Tool/FileCryptor.py

Lines changed: 86 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,84 @@
11
import threading
22
import tkinter as tk
3-
from tkinter import filedialog, messagebox, scrolledtext
3+
from tkinter import filedialog, messagebox, scrolledtext, simpledialog
44
import ttkbootstrap as tb
55
from cryptography.fernet import Fernet
6+
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
7+
from cryptography.hazmat.primitives import hashes
8+
from cryptography.hazmat.backends import default_backend
69
import os
710
import sys
811
import base64
9-
import hashlib
1012

11-
def resource_path(file_name):
12-
base_path = getattr(sys, '_MEIPASS', os.path.dirname(os.path.abspath(__file__)))
13-
return os.path.join(base_path, file_name)
14-
15-
def derive_key(password: str) -> bytes:
16-
"""Derive a Fernet key from a password using SHA256"""
17-
return base64.urlsafe_b64encode(hashlib.sha256(password.encode()).digest())
13+
# ===== Secure Key Derivation =====
14+
def derive_key(password: str, salt: bytes) -> bytes:
15+
"""Derive a Fernet key from a password using PBKDF2HMAC and salt."""
16+
kdf = PBKDF2HMAC(
17+
algorithm=hashes.SHA256(),
18+
length=32,
19+
salt=salt,
20+
iterations=390_000,
21+
backend=default_backend()
22+
)
23+
return base64.urlsafe_b64encode(kdf.derive(password.encode()))
1824

1925
class FileEncryptor:
2026
def __init__(self, root):
2127
self.root = root
22-
self.root.title("FileCryptor - File Encryption Tool")
28+
self.root.title("FileCryptor - Secure File Encryption")
2329
self.root.geometry("1100x660")
24-
# self.root.iconbitmap(resource_path("logo.ico"))
2530

26-
# ===== Buttons Frame =====
31+
# ===== Buttons =====
2732
button_frame = tb.Frame(self.root)
2833
button_frame.pack(pady=10)
2934

30-
self.encrypt_btn = tb.Button(
31-
button_frame, text="🔒 Encrypt Files", bootstyle="success-outline", width=25,
32-
command=lambda: self.start_process(encrypt=True)
33-
)
35+
self.encrypt_btn = tb.Button(button_frame, text="🔒 Encrypt Files", bootstyle="success-outline", width=25,
36+
command=lambda: self.start_process(encrypt=True))
3437
self.encrypt_btn.grid(row=0, column=0, padx=5)
3538

36-
self.decrypt_btn = tb.Button(
37-
button_frame, text="🔓 Decrypt Files", bootstyle="info-outline", width=25,
38-
command=lambda: self.start_process(encrypt=False)
39-
)
39+
self.decrypt_btn = tb.Button(button_frame, text="🔓 Decrypt Files", bootstyle="info-outline", width=25,
40+
command=lambda: self.start_process(encrypt=False))
4041
self.decrypt_btn.grid(row=0, column=1, padx=5)
4142

4243
self.cancel_event = threading.Event()
43-
44-
self.cancel_btn = tb.Button(
45-
button_frame, text="❌ Cancel", bootstyle="danger-outline", width=20,
46-
state=tk.DISABLED, command=self.cancel_process
47-
)
44+
self.cancel_btn = tb.Button(button_frame, text="❌ Cancel", bootstyle="danger-outline", width=20,
45+
state=tk.DISABLED, command=self.cancel_process)
4846
self.cancel_btn.grid(row=0, column=2, padx=5)
4947

50-
self.clear_btn = tb.Button(
51-
button_frame, text="🧹 Clear Log", bootstyle="warning-outline", width=20,
52-
command=self.clear_log
53-
)
48+
self.clear_btn = tb.Button(button_frame, text="🧹 Clear Log", bootstyle="warning-outline", width=20,
49+
command=self.clear_log)
5450
self.clear_btn.grid(row=0, column=3, padx=5)
5551

56-
self.about_btn = tb.Button(
57-
button_frame, text="ℹ️ About", bootstyle="secondary-outline", width=20,
58-
command=self.show_about_guide
59-
)
52+
self.about_btn = tb.Button(button_frame, text="ℹ️ About", bootstyle="secondary-outline", width=20,
53+
command=self.show_about_guide)
6054
self.about_btn.grid(row=0, column=4, padx=5)
6155

62-
# ===== Log Area =====
56+
# ===== Log area =====
6357
self.log_area = scrolledtext.ScrolledText(self.root, wrap=tk.WORD, font=("Arial", 12))
6458
self.log_area.pack(expand=True, fill=tk.BOTH, padx=10, pady=10)
6559

66-
# ===== Progress Bar Frame =====
60+
# ===== Progress =====
6761
progress_frame = tb.Frame(self.root)
6862
progress_frame.pack(fill=tk.X, padx=10, pady=(0, 10))
69-
7063
self.progress_label = tb.Label(progress_frame, text="Ready")
7164
self.progress_label.pack(side=tk.LEFT, padx=(0, 10))
72-
7365
self.progress = tb.Progressbar(progress_frame, bootstyle="success-striped", mode="determinate")
7466
self.progress.pack(side=tk.RIGHT, fill=tk.X, expand=True)
7567

68+
# ===== Logging =====
69+
def _log(self, msg):
70+
self.log_area.insert(tk.END, msg + "\n")
71+
self.log_area.see(tk.END)
72+
73+
def clear_log(self):
74+
self.log_area.delete(1.0, tk.END)
75+
76+
# ===== Cancel =====
7677
def cancel_process(self):
7778
self.cancel_event.set()
7879
self.progress_label.config(text="Cancelling...")
7980

81+
# ===== UI Reset =====
8082
def _reset_ui(self):
8183
self.progress["value"] = 0
8284
self.progress_label.config(text="Ready")
@@ -85,73 +87,60 @@ def _reset_ui(self):
8587
self.cancel_btn.config(state=tk.DISABLED)
8688
self.cancel_event.clear()
8789

88-
def _update_progress(self, value):
89-
self.progress["value"] = value
90-
self.progress_label.config(text=f"Processing... {int(value)}%")
91-
92-
def _log(self, message):
93-
self.log_area.insert(tk.END, message + "\n")
94-
self.log_area.see(tk.END)
95-
96-
def clear_log(self):
97-
self.log_area.delete(1.0, tk.END)
98-
9990
# ===== Worker =====
10091
def _process_worker(self, file_paths, password, encrypt=True):
101-
try:
102-
key = derive_key(password)
103-
fernet = Fernet(key)
104-
total_files = len(file_paths)
105-
step = 100 / total_files
106-
current_progress = 0
107-
108-
for file_path in file_paths:
109-
if self.cancel_event.is_set():
110-
self.root.after(0, self._reset_ui)
111-
self._log("Process cancelled.")
112-
return
113-
114-
filename = os.path.basename(file_path)
115-
try:
116-
with open(file_path, "rb") as f:
117-
data = f.read()
118-
119-
if encrypt:
120-
processed_data = fernet.encrypt(data)
121-
out_file = file_path + ".enc"
122-
else:
123-
processed_data = fernet.decrypt(data)
124-
if file_path.endswith(".enc"):
125-
out_file = file_path[:-4]
126-
else:
127-
out_file = file_path + ".dec"
128-
92+
total_files = len(file_paths)
93+
step = 100 / total_files
94+
current_progress = 0
95+
96+
for file_path in file_paths:
97+
if self.cancel_event.is_set():
98+
self.root.after(0, self._reset_ui)
99+
self._log("Process cancelled.")
100+
return
101+
102+
filename = os.path.basename(file_path)
103+
try:
104+
with open(file_path, "rb") as f:
105+
data = f.read()
106+
107+
if encrypt:
108+
salt = os.urandom(16)
109+
key = derive_key(password, salt)
110+
fernet = Fernet(key)
111+
encrypted = fernet.encrypt(data)
112+
out_file = file_path + ".enc"
129113
with open(out_file, "wb") as f:
130-
f.write(processed_data)
131-
132-
self._log(f"{'Encrypted' if encrypt else 'Decrypted'}: {filename}")
114+
f.write(salt + encrypted) # prepend salt
115+
else:
116+
salt = data[:16]
117+
encrypted_data = data[16:]
118+
key = derive_key(password, salt)
119+
fernet = Fernet(key)
120+
decrypted = fernet.decrypt(encrypted_data)
121+
out_file = file_path[:-4] if file_path.endswith(".enc") else file_path + ".dec"
122+
with open(out_file, "wb") as f:
123+
f.write(decrypted)
133124

134-
except Exception as e:
135-
self._log(f"Error processing {filename}: {str(e)}")
125+
self._log(f"{'Encrypted' if encrypt else 'Decrypted'}: {filename}")
136126

137-
current_progress += step
138-
self.root.after(0, self._update_progress, current_progress)
127+
except Exception as e:
128+
self._log(f"Error processing {filename}: {str(e)}")
139129

140-
self.root.after(0, self._reset_ui)
141-
self._log("Process completed.")
130+
current_progress += step
131+
self.root.after(0, self.progress.step, step)
142132

143-
except Exception as e:
144-
self.root.after(0, self._reset_ui)
145-
self._log(f"Fatal error: {str(e)}")
133+
self.root.after(0, self._reset_ui)
134+
self._log("Process completed.")
146135

147-
# ===== Start Process =====
136+
# ===== Start =====
148137
def start_process(self, encrypt=True):
149138
action = "Encrypt" if encrypt else "Decrypt"
150139
file_paths = filedialog.askopenfilenames(title=f"Select files to {action}")
151140
if not file_paths:
152141
return
153142

154-
password = tk.simpledialog.askstring("Password", f"Enter password to {action} files:", show="*")
143+
password = simpledialog.askstring("Password", f"Enter password to {action} files:", show="*")
155144
if not password:
156145
messagebox.showwarning("Password required", "Operation cancelled. Password is required!")
157146
return
@@ -165,46 +154,33 @@ def start_process(self, encrypt=True):
165154

166155
threading.Thread(target=self._process_worker, args=(file_paths, password, encrypt), daemon=True).start()
167156

168-
# ===== About / Guide =====
157+
# ===== About =====
169158
def show_about_guide(self):
170159
guide_window = tb.Toplevel(self.root)
171160
guide_window.title("📘 About / Guide")
172161
guide_window.geometry("600x480")
173162
guide_window.resizable(False, False)
174163
guide_window.grab_set()
175-
guide_window.attributes("-toolwindow", True)
176-
177-
self.root.update_idletasks()
178-
root_x = self.root.winfo_x()
179-
root_y = self.root.winfo_y()
180-
root_width = self.root.winfo_width()
181-
root_height = self.root.winfo_height()
182-
win_width = 600
183-
win_height = 480
184-
pos_x = root_x + (root_width // 2) - (win_width // 2)
185-
pos_y = root_y + (root_height // 2) - (win_height // 2)
186-
guide_window.geometry(f"{win_width}x{win_height}+{pos_x}+{pos_y}")
187164

188165
frame = tb.Frame(guide_window, padding=10)
189166
frame.pack(fill="both", expand=True)
190167

191168
sections = {
192169
"About FileCryptor": (
193170
"FileCryptor is a secure and easy-to-use desktop tool for encrypting and decrypting files.\n"
194-
"It uses password-based encryption (Fernet symmetric encryption) to protect your data."
171+
"It uses password-based encryption (PBKDF2 + Fernet) to protect your data."
195172
),
196173
"Key Features": (
197174
"- Encrypt and decrypt multiple files\n"
198-
"- Password protected\n"
175+
"- Password protected with secure KDF\n"
199176
"- Cancel operations anytime\n"
200177
"- Real-time progress bar\n"
201178
"- Modern, clean interface"
202179
),
203-
"Built With": (
204-
"- Python\n"
205-
"- Tkinter & ttkbootstrap\n"
206-
"- cryptography library\n"
207-
"- Multithreading for responsive UI"
180+
"Use Case Example": (
181+
"- Encrypt your personal documents folder before uploading to cloud storage\n"
182+
"- Decrypt project files shared by a colleague\n"
183+
"- Securely archive sensitive PDFs or images"
208184
),
209185
"Developer": (
210186
"Developed by MateTools\n"

0 commit comments

Comments
 (0)