diff --git a/generator.py b/generator.py index b2d94de..391491a 100644 --- a/generator.py +++ b/generator.py @@ -1,15 +1,15 @@ from time import sleep from secrets import choice from zxcvbn import zxcvbn -from pandas import DataFrame as DF + class Generator: def __init__(self): self.passlist = [] self.results = [] - self.df = DF() - self.password_length=0 - def _generate_password(self,length): + self.password_length = 0 + + def _generate_password(self, length): # Define the character sets to be used in the password generation alphabets = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM' symbols = '`1234567890-=[]\\;\',./~!@#$%^&*()_+{}|:"<>?' @@ -26,7 +26,7 @@ def _generate_password(self,length): return ''.join(password) - def _jumble_string(self,string): + def _jumble_string(self, string): chars = list(string) jumbled_string = '' l = len(chars) @@ -37,53 +37,68 @@ def _jumble_string(self,string): return jumbled_string - def generator(self,n,password_length): + def generator(self, n, password_length): if not isinstance(n, int): raise TypeError("n must be an integer.") if not isinstance(password_length, int): raise TypeError("password_length must be an integer.") - self.__init__()#clearing memory to clear out previously generated suggestions - self.password_length=password_length - self.passlist = [self._generate_password(password_length) for _ in range(n)] - self.strengthEvaluator() + if password_length < 11: + return [] # Return an empty list if the password length is less than 11 + self.__init__() # Clear memory to clear out previously generated suggestions + self.password_length = password_length + num_generated = 0 # Counter for the number of generated passwords with score 4 + #gap_counter = 0 # Counter to track the gap between successful score 4 generations + + while num_generated < n: + password = self._generate_password(password_length) + insights = zxcvbn(password) + if insights['score'] == 4: + self.passlist.append([password,insights['crack_times_seconds']['offline_fast_hashing_1e10_per_second']]) + num_generated += 1 + gap_counter = 0 # Reset the gap counter since a score 4 password was generated + else: + gap_counter += 1 + + def strengthEvaluator(self): - """Accepts a list of passwords and returns insights in the form of list of the passwords and their score - [on a scale of 0 to 4] and respective cracktime estimates at different brute force speeds.""" self.results = [] for i, password in enumerate(self.passlist): if not isinstance(password, str): raise ValueError("The Password list must contain string literals") insights = zxcvbn(password) - self.results.append({ - 'Sl.No.': i + 1, - 'suggested password': password, - 'score': insights['score'], - '@100/hr': insights['crack_times_display']['online_throttling_100_per_hour'], - '@36,000/hr': insights['crack_times_display']['online_no_throttling_10_per_second'], - '@196,000/hr': insights['crack_times_display']['offline_slow_hashing_1e4_per_second'], - '@792.9M/hr': insights['crack_times_display']['offline_fast_hashing_1e10_per_second'] - }) - - self.df = DF(self.results) + result_entry = [ + i + 1, + password, + insights['score'], + insights['crack_times_seconds']['offline_fast_hashing_1e10_per_second'] + ] + self.results.append(result_entry) def get_top_passwords(self, n): - """returns a subset of the original self.results containing top n suggestions sorted in descending order of strength""" - df = self.df[self.df['score'] == 4] - df = df.sort_values(by='score', ascending=False) # Sort in descending order based on 'score' - top_passwords = df.head(n).to_dict('records') # Convert the top 'n' rows to a list of dictionaries - return top_passwords + """Returns a subset of the original self.passlist containing the top 'n' suggestions sorted by longest crack times.""" + sorted_passwords = sorted(self.passlist, key=lambda p: p[1], reverse=True) + return sorted_passwords[:n] - def display_suggestions(self, n):#displays top n suggestions - # Print the header - if not isinstance(n,int): - raise ValueError("n must be integer type") - print(f'{"Sl.No.":<8} {"suggested password":<{self.password_length+5}} {"score":<10} {"@100/hr":<10} {"@36,000/hr":<10} {"@196,000/hr":<10} {"@792.9M/hr":<10}') + + + + def display_suggestions(self, n): top = self.get_top_passwords(n) + + # Determine the maximum password length in the top suggestions + max_length = max(len(password) for password, _ in top) + + # Print the header + print(f'{"Sl.No.":<8} {"suggested password":<{max_length+5}} {"score":<10} {"Vs e^10/sec Hashing[Hrs].":<27}') + + # Print the data - for row in top: - print(f'{row["Sl.No."]:<8} {row["suggested password"]:{self.password_length+5}} {row["score"]:<10} {row["@100/hr"]:<10} {row["@36,000/hr"]:<10} {row["@196,000/hr"]:<13} {row["@792.9M/hr"]:<10}') + for i, (password, crack_time) in enumerate(top, start=1): + print(f'{i:<8} {password:<{max_length+5}} {4:<10} {crack_time / 3600:<.4f}') + + def main(): """An example of how this class can be used""" @@ -97,7 +112,7 @@ def main(): print('Length must be between 15 and 25. Try again.') continue else: - break#if specified length is within hard coded constraints + break # If specified length is within hard-coded constraints except ValueError: print('Invalid input. Try again.\n\n') @@ -108,7 +123,7 @@ def main(): print('Only values between 1 and 100 (inclusive) are allowed.') continue else: - break#if number of suggestions requested is within hard-coded limits + break # If the number of suggestions requested is within hard-coded limits except ValueError: print('Invalid input. Try again.\n\n') @@ -121,5 +136,6 @@ def main(): sleep(.999) print(f'\rAutoterminating in {30 - i - 1} seconds', end='', flush=True) + if __name__ == "__main__": main()