From a39b0f1d7df5ed4517d2d773e9d3d5863aa80cf9 Mon Sep 17 00:00:00 2001 From: Nayan Chandwani <102764937+Koladweep@users.noreply.github.com> Date: Tue, 18 Jul 2023 00:32:22 +0530 Subject: [PATCH] removed dependency on pandas... updated sorting logic. preview for pypi I have removed any unnecessary metrics from generated metrics and kept only the most brutal metric. removed dependency on pandas for the simple sorting operation. also the main method kind of specifies the best use case. also hard coded the minimum password length for score 4 for passwords genereated with this script i.e. 11 below which the score is less than 4. so this script in effect cannot generate passwords below score 4 or length 11. --- generator.py | 90 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 53 insertions(+), 37 deletions(-) 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()