-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpwgenie.py
361 lines (288 loc) · 13.5 KB
/
pwgenie.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
import tkinter as tk
from tkinter import messagebox
import secrets
import string
import base64
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.fernet import Fernet
# Key derivation function setup
def derive_key(master_key, salt):
kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
iterations=100000,
salt=salt,
length=32,
backend=default_backend()
)
key = kdf.derive(master_key.encode())
return key
# GUI Setup
app = tk.Tk()
app.title("Password Genie")
# Set the size of the main window
app.geometry("300x400")
# Set the system icon
#app.iconphoto(True, tk.PhotoImage(file="icon.png"))
app.iconbitmap("icon.ico")
# Entry for the user's master pass-key
label_master_key = tk.Label(app, text="Master Pass-Key:")
entry_master_key = tk.Entry(app, show="*") # Use show="*" to hide the entered characters
# Entry for the password (for hashing and verification)
label_password = tk.Label(app, text="Password:")
entry_password = tk.Entry(app, show="*")
# Entry for the password length (for generating passwords)
label_length = tk.Label(app, text="Password Length:")
entry_length = tk.Entry(app)
entry_length.insert(0, "12") # Default value
# Entry for character requirements (for generating passwords)
label_requirements = tk.Label(app, text="Character Requirements:")
entry_requirements = tk.Entry(app)
entry_requirements.insert(0, "letters,digits,punctuation") # Default value
# Entry for the platform (for saving and retrieving passwords)
label_platform = tk.Label(app, text="Platform:")
entry_platform = tk.Entry(app)
# Label for displaying the generated password
label_password_text = tk.Label(app, text="Generated Password:")
# Textbox to display the generated password
password_text = tk.Entry(app, state='readonly', width=20)
# Button to copy the generated password to the clipboard
def copy_to_clipboard():
app.clipboard_clear()
app.clipboard_append(password_text.get())
app.update()
messagebox.showinfo("Copied to Clipboard", "The password was copied to the Clipboard.")
# Flag to check if the master password is set
master_password_set = None # Initialize as None
# Function to verify the master password
def verify_master_password():
global master_password_set
try:
with open('master_key.txt', 'r') as file:
line = file.readline()
if line:
parts = line.split(':')
salt_hex = parts[0].strip()
hashed_master_key_stored = parts[1].strip()
# Check if the master password file is found and has data
if salt_hex and hashed_master_key_stored:
master_password_set = True
else:
master_password_set = False
messagebox.showinfo("Master Password Not Set", "The master password is not set. Please set the initial master password by running the set-master-password utility.")
return
else:
master_password_set = False
messagebox.showinfo("Master Password Not Set", "The master password is not set. Please set the initial master password by running the set-master-password utility.")
return
except Exception as e:
master_password_set = False
messagebox.showerror("Error", f"An error occurred while verifying the master password: {str(e)}")
master_key = entry_master_key.get()
if not master_password_set:
# If not set, prompt the user to set it
messagebox.showinfo("Master Password Not Set", "The master password is not set. Please set the initial master password by running the set-master-password utility.")
return
# Continue with the existing verification logic
try:
with open('master_key.txt', 'r') as file:
line = file.readline()
if line:
parts = line.split(':')
salt_hex = parts[0].strip()
hashed_master_key_stored = parts[1].strip()
salt = bytes.fromhex(salt_hex)
hashed_master_key_entered = derive_key(master_key, salt).hex()
if hashed_master_key_stored == hashed_master_key_entered:
messagebox.showinfo("Master Password Verified", "Master password verified successfully.")
else:
messagebox.showerror("Error", "Incorrect master password.")
master_password_set = False # Set to False if verification fails
else:
messagebox.showerror("Error", "Master password not set. Please set the initial master password by running the set-master-password utility.")
master_password_set = False # Set to False if file is empty
except Exception as e:
messagebox.showerror("Error", f"An error occurred while verifying the master password: {str(e)}")
master_password_set = False # Set to False if an error occurs
# Function to handle password generation
def generate_password():
global master_password_set
if not master_password_set:
messagebox.showerror("Error", "Please set the initial master password by running the set-master-password utility.")
return
length_str = entry_length.get()
requirements = entry_requirements.get()
try:
length = int(length_str)
except ValueError:
messagebox.showerror("Error", "Please enter a valid numeric value for password length.")
return
if length <= 0:
messagebox.showerror("Error", "Password length must be greater than 0.")
return
try:
characters = string.ascii_letters + string.digits + string.punctuation
password = ''.join(secrets.choice(characters) for _ in range(length))
password_text.config(state='normal') # Enable editing
password_text.delete(0, tk.END) # Clear previous content
password_text.insert(tk.END, password)
password_text.config(state='readonly') # Disable editing
label_password_text.config(text="Generated Password:")
messagebox.showinfo("Generated Password", f"Your password is: {password}")
except Exception as e:
messagebox.showerror("Error", f"An error occurred while generating the password: {str(e)}")
# Function to handle password hashing
def hash_password():
global master_password_set
if not master_password_set:
messagebox.showerror("Error", "Please set the initial master password by running the set-master-password utility.")
return
password = entry_password.get()
if not password:
messagebox.showerror("Error", "Please enter a password to hash.")
return
# Hash the password using SHA-256
digest = hashes.Hash(hashes.SHA256(), backend=default_backend())
digest.update(password.encode())
hashed_password = digest.finalize()
messagebox.showinfo("Hashed Password", f"The hashed password is: {hashed_password.hex()}")
# Function to handle saving the password
def save_password():
global master_password_set
if not master_password_set:
messagebox.showerror("Error", "Please set the initial master password by running the set-master-password utility.")
return
master_key = entry_master_key.get()
password = entry_password.get()
platform = entry_platform.get()
if not master_key or not password or not platform:
messagebox.showerror("Error", "Please fill in all fields.")
return
salt = secrets.token_bytes(16) # Generate a random salt
key = derive_key(master_key, salt)
# Ensure key is 32 bytes and base64-encoded
key = base64.urlsafe_b64encode(key)
key = key.ljust(32, b'=')
# Encrypt and save the password
try:
cipher_suite = Fernet(key)
encrypted_password = cipher_suite.encrypt(password.encode())
with open('passwords.txt', 'a') as file:
file.write(f"{platform}: {salt.hex()}:{encrypted_password.decode()}\n")
messagebox.showinfo("Password Saved", "Password saved successfully.")
except Exception as e:
messagebox.showerror("Error", f"An error occurred while saving the password: {str(e)}")
# Function to handle retrieving and decrypting the password
def retrieve_password():
global master_password_set
if not master_password_set:
messagebox.showerror("Error", "Please set the initial master password by running the set-master-password utility.")
return
master_key = entry_master_key.get()
platform = entry_platform.get()
if not master_key or not platform:
messagebox.showerror("Error", "Please enter your master password and platform.")
return
try:
with open('passwords.txt', 'r') as file:
for line in file:
if platform in line:
parts = line.split(':')
salt_hex = parts[1].strip()
encrypted_password = parts[2].strip()
salt = bytes.fromhex(salt_hex)
key = derive_key(master_key, salt)
# Ensure key is 32 bytes
key = key[:32]
cipher_suite = Fernet(base64.urlsafe_b64encode(key))
decrypted_password = cipher_suite.decrypt(encrypted_password.encode()).decode()
password = decrypted_password
# Update password_text widget and change its label
password_text.config(state='normal') # Enable editing
password_text.delete(0, tk.END) # Clear previous content
password_text.insert(tk.END, decrypted_password)
label_password_text.config(text="Retrieved Password:")
messagebox.showinfo("Decrypted Password", f"Your password for {platform} is: {decrypted_password}")
return
messagebox.showerror("Error", f"No password found for the platform: {platform}")
except Exception as e:
messagebox.showerror("Error", f"An error occurred while retrieving the password: {str(e)}")
# ToolTip class for adding tooltips to widgets
class ToolTip:
def __init__(self, widget, text):
self.widget = widget
self.text = text
self.tooltip = None
self.widget.bind("<Enter>", self.enter)
self.widget.bind("<Leave>", self.leave)
def enter(self, event):
x, y, _, _ = self.widget.bbox("insert")
x += self.widget.winfo_rootx() + 25
y += self.widget.winfo_rooty() + 25
self.tooltip = tk.Toplevel(self.widget)
self.tooltip.wm_overrideredirect(True)
self.tooltip.wm_geometry(f"+{x}+{y}")
label = tk.Label(self.tooltip, text=self.text, background="#ffffe0", relief="solid", borderwidth=1)
label.pack(ipadx=1)
def leave(self, event):
if self.tooltip:
self.tooltip.destroy()
# Function to display the About dialog
def show_about_dialog():
about_text = (
"Password Genie\n"
"Version 1.0.12\n"
"Created by Kaotick Jay\n"
"License: GNU/GPL3\n"
"Copyright (c) 2023 by Kaotick Jay\n"
"GitHub: https://github.com/kaotickj"
)
messagebox.showinfo("About", about_text)
# Menu bar
menu_bar = tk.Menu(app)
app.config(menu=menu_bar)
# About menu
about_menu = tk.Menu(menu_bar, tearoff=0)
menu_bar.add_cascade(label="About", menu=about_menu)
about_menu.add_command(label="About Password Genie", command=show_about_dialog)
# Place widgets using the place method
label_master_key.place(x=10, y=10)
entry_master_key.place(x=150, y=10)
ToolTip(entry_master_key, "Enter your master pass-key")
label_password.place(x=10, y=40)
entry_password.place(x=150, y=40)
ToolTip(entry_password, "Enter a password for hashing or leave it blank")
label_length.place(x=10, y=70)
entry_length.place(x=150, y=70)
ToolTip(entry_length, "Enter the desired password length (default is 12)")
label_requirements.place(x=10, y=100)
entry_requirements.place(x=150, y=100)
ToolTip(entry_requirements, "Enter character requirements (default is letters, digits, punctuation)")
label_platform.place(x=10, y=130)
entry_platform.place(x=150, y=130)
ToolTip(entry_platform, "Enter the platform for saving or retrieving the password")
button_generate = tk.Button(app, text="Generate Password", command=generate_password)
ToolTip(button_generate, "Generate a random password")
button_generate.place(x=10, y=160)
button_hash = tk.Button(app, text="Hash Password", command=hash_password)
ToolTip(button_hash, "Hash the entered password")
button_hash.place(x=170, y=160)
button_save = tk.Button(app, text="Save Password", command=save_password)
ToolTip(button_save, "Save the password for the specified platform")
button_save.place(x=10, y=190)
button_retrieve = tk.Button(app, text="Retrieve Password", command=retrieve_password)
ToolTip(button_retrieve, "Retrieve and decrypt the password for the specified platform")
button_retrieve.place(x=170, y=190)
button_verify_master_password = tk.Button(app, text="Verify Master Password", command=verify_master_password)
ToolTip(button_verify_master_password, "Verify the entered master password")
#button_verify_master_password.place(x=150, y=220)
button_verify_master_password.place(x=10, y=220)
# Textbox to display the generated password
label_password_text.place(x=10, y=260)
password_text.place(x=150, y=260)
# Button to copy generated password to clipboard
copy_button = tk.Button(app, text="Copy to Clipboard", command=copy_to_clipboard)
copy_button.place(x=10, y=290)
# Main Event Loop
app.mainloop()