2
\$\begingroup\$

I am a beginner in Python and I have attempted to create a small script/program which allows the user to do the following:

  1. Generate a single random password

  2. Generate a number of passwords specified by the user of a certain length specified by the user to a text file in a location of their choice

  3. Test the strength of a password that is inputted by the user.

  4. Test the strength of a text file selected by the user containing passwords (the text file created by the program/script in feature 2 would be ideal to test).

The code most definitely could be much improved. I would be extremely grateful if anyone gets free time to look through the code and suggest improvements that could be made to the code to help me develop my understanding better.

password.py (main file)

try:
 import tkinter as tk
 from tkinter import *
 from tkinter import filedialog
 import random
 import os
 import passfunctions
except ImportError:
 raise ImportError('function is not here')
def gui_input(prompt):
 root = tk.Toplevel()
 # this will contain the entered string, and will
 # still exist after the window is destroyed
 var = tk.StringVar()
 # create the dialog
 label = tk.Label(root, text=prompt)
 entry = tk.Entry(root, textvariable=var)
 label.pack(side="left", padx=(20, 0), pady=20)
 entry.pack(side="right", fill="x", padx=(0, 20), pady=20, expand=True)
 # Let the user press the return key to destroy the gui
 entry.bind("<Return>", lambda event: root.destroy())
 # this will wait until the window is destroyed
 root.wait_window()
 # after the window has been destroyed, we can't access
 # the entry widget, but we _can_ access the associated
 # variable
 value = var.get()
 return value
def generatesinglepass():
 
 displaypasswords.delete(1.0, END) #deletes any data that may be in tkinter Text widget from other functions
 chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@£$%^&*().,?0123456789' #list of characters which can be used to generate password#list of characters which can be used to generate password#list of characters which can be used to generate password
 
 number = int('1') # set number of passwords to be generat
 
 
 while True:
 try:
 length = int(gui_input("Please enter how long you would like each password to be (e.g. 20)" )) # prompts user for length of password
 except ValueError:
 print("Not a valid number") # prints error if user hasn't enteted a valid value, (e.g. 6)
 continue
 else:
 break
 print('\nhere are the generated password:')
 
 for pwd in range(number):
 password = ''
 for c in range(length):
 password += random.choice(chars)
 print(password)
 displaypasswords.insert(1.0 , password) #display single generated password in text tkinter widget
def generatepass():
 displaypasswords.delete(1.0, END) #deletes any data that may be in tkinter Text widget from other functions
 chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@£$%^&*().,?0123456789' #list of characters which can be used to generate password#list of characters which can be used to generate password
 while True:
 try:
 number = int(gui_input("Please enter the number of passwords you would like to generate (e.g. 2)" )) # prompts user for number of passwords
 except ValueError:
 print("Not a valid number")
 continue
 else:
 break
 while True:
 try:
 length = int(gui_input("Please enter how long you would like each password to be (e.g. 20)" )) # prompts user for length of passwords
 except ValueError:
 print("Not a valid number")
 continue
 else:
 break
 print('\nhere are the generated passwords:')
 savepass = filedialog.asksaveasfilename(initialdir="/home", title = "Enter save file name",filetypes = (("text files","*.txt"),("all files","*.*")))
 with open(savepass ,"w") as text_file: # open text file selected by user in pevious dialog to write generated passwords to.
 for pwd in range(number):
 password = ''
 for c in range(length):
 password += random.choice(chars)
 print(password)
 text_file.writelines(password+"\n") # write passwords to generatepass.txt file
 
 displaypasswords.insert('end', password+"\n") # display passwords in tkinter text widget
 
 displaypasswords.insert('end', "\nPassword's have been outputted to text file")
 
def strength(): # password strength check function for single user entered password
 displaypasswords.delete(1.0, END) #deletes any data that may be in tkinter Text widget from other functions
 password = gui_input("Please enter password you would like to check strength of" ) # prompts user to enter password
 def strongPassword(password):
 
 passfunctions.regexcompile(password) # runs regex commands from passfunctions.py file to test password strength
 if passfunctions.regexcompile(password) == True:
 print("Strong Password")
 displaypasswords.insert('end', "Password is strong")
 else:
 print("This is not a strong password")
 displaypasswords.insert('end', "Password is not strong")
 
def multiplestrength(): # password strength check function from selected text file containing passwords
 displaypasswords.delete(1.0, END) #deletes any data that may be in tkinter Text widget from other functions
 
 def strong_password(password): # the function name should be snake case
 passfunctions.regexcompile(password)
 textfile = filedialog.askopenfilename(initialdir="/home", title = "Select text file containing passwords",filetypes = (("text files","*.txt"),("all files","*.*")))
 with open(textfile, mode="r", encoding="utf-8") as pass_file: # Open fle containing passwords to read
 if os.stat(textfile).st_size == 0:
 print("no password in file")
 else:
 savefile = filedialog.asksaveasfilename(initialdir="/home", title = "Enter save file name for pass strength results",filetypes = (("text files","*.txt"),("all files","*.*"))) # open file to save password strength results to which was select in previous dialog
 with open(savefile, "w") as strength_file:
 for line in pass_file.readlines(): # Read all lines one-by-one
 print("\nPassword: {}".format(line.strip()), file=strength_file) # Print the current password ("strip" removes the whitespace characters from string).
 displaypasswords.insert('end',"\nPassword: {}".format(line.strip())) # Print the current password ("strip" removes the whitespace characters from string).
 if passfunctions.regexcompile(line): # This statement is True if the "strong_password" function returns True
 displaypasswords.insert('end',"\nStrong Password\n") 
 print("Strong Password", file=strength_file) 
 continue # Get the next element (line of file)
 displaypasswords.insert('end', "\nThis is not a strong password\n") # Else statement is not needed because the "if" contains a continue
 print("This is not a strong password", file=strength_file)
 
def quit():
 root.quit()
root = tk.Tk()
root.geometry("350x350")
root.wm_title("Password Tools")
maintitle = tk.Label(root, text = 'Password Tools', font = ('Comic Sans MS',18))
generatesingle = tk.Button(root, text="Generate Single Password", command=generatesinglepass)
generatemulti = tk.Button(root, text="Generate Multiple Password to Text File", command=generatepass)
checkstrength = tk.Button(root, text = "Check Password Strength", command=strength)
checkstrengthfromtext = tk.Button(root, text = "Check Password Strength from Text File", command=multiplestrength)
quit = tk.Button(root, text = "Quit Program", command=quit)
outputlabel = tk.Label(root, text = "Output")
displaypasswords = Text(root)
maintitle.pack()
generatesingle.pack()
generatemulti.pack()
checkstrength.pack()
checkstrengthfromtext.pack()
quit.pack()
outputlabel.pack()
displaypasswords.pack() 
root.mainloop()

passfunctions.py (this contains a reused piece of code in two of the functions and is imported into the main password.py file

import re
def regexcompile(password):
 
 if passRegex1.search(password) == None:
 return False
 if passRegex2.search(password) == None:
 return False
 if passRegex3.search(password) == None:
 return False
 if passRegex4.search(password) == None:
 return False
 else:
 return True
passRegex1 = re.compile(r'\w{8,}')
passRegex2 = re.compile(r'\d+')
passRegex3 = re.compile(r'[a-z]')
passRegex4 = re.compile(r'[A-Z]') 
Ben A
10.7k5 gold badges37 silver badges101 bronze badges
asked Jul 9, 2020 at 23:45
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Reliability

I get that this program is meant for you to continue learning python, but please don't use any passwords generated by this program. Especially since your strength check is really weak. I.E, this function that checks the strength thinks Password123 is a strong password. Don't use it :-).

Checking password strength

This function can be reduced to the following:

import re
def check_password_strength(password: str) -> bool:
 tests = [
 re.compile(r'\w{8,}'),
 re.compile(r'\d+'),
 re.compile(r'[a-z]'),
 re.compile(r'[A-Z]')
 ]
 return not any(test.search(password) == None for test in tests)

Instead of creating individual variables for each regex, make a list and loop through it checking the password against each value in the list.

Take a look at zxcvbn, which is a password strength tester written by Dropbox. It's in javascript, but if you understand the main algorithm you'll be able to write it in python.

Type Hints

These allow you to display what types of parameters are accepted and what types are returned by your functions. Take a look at the function above for an example. Accepts password as a str, and returns a bool(ean) value.

Creating strings

This

password = ''
for c in range(length):
 password += random.choice(chars)
print(password)

can be written like this (thanks Graipher)

password = ''.join(random.choices(chars, k=length))

The _ just means the variable in the loop isn't used, and should be ignored.

answered Jul 10, 2020 at 0:04
\$\endgroup\$
3
  • \$\begingroup\$ Thanks for this very useful feedback. I will definitely look into the strength check being weak. \$\endgroup\$ Commented Jul 10, 2020 at 0:54
  • \$\begingroup\$ ''.join(random.choices(chars, k=length)) might be even better. \$\endgroup\$ Commented Jul 13, 2020 at 9:14
  • \$\begingroup\$ @Graipher I didn't know about that quirk, I'll edit accordingly. Thanks! \$\endgroup\$ Commented Jul 14, 2020 at 6:06

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.