| skipped 28 lines |
29 | 29 | | # (all optional futures for 2.7) |
30 | 30 | | from __future__ import print_function, absolute_import, division, unicode_literals |
31 | 31 | | |
32 | | - | __version__ = "0.17.6" |
| 32 | + | __version__ = "0.17.7" |
33 | 33 | | __ordering_version__ = b"0.6.4" # must be updated whenever password ordering changes |
34 | 34 | | |
35 | 35 | | import sys, argparse, itertools, string, re, multiprocessing, signal, os, cPickle, gc, \ |
| skipped 4062 lines |
4098 | 4098 | | global password_dups, token_combination_dups, passwordlist_warnings |
4099 | 4099 | | password_dups = token_combination_dups = None |
4100 | 4100 | | passwordlist_warnings = 0 |
| 4101 | + | # (re)set the min_typos argument default values to 0 |
| 4102 | + | capslock_typos_generator.func_defaults = (0,) |
| 4103 | + | swap_typos_generator .func_defaults = (0,) |
| 4104 | + | simple_typos_generator .func_defaults = (0,) |
| 4105 | + | insert_typos_generator .func_defaults = (0,) |
4101 | 4106 | | # |
4102 | 4107 | | def password_generator(chunksize = 1, only_yield_count = False): |
4103 | 4108 | | assert chunksize > 0, "password_generator: chunksize > 0" |
4104 | 4109 | | # Used to communicate between typo generators the number of typos that have been |
4105 | 4110 | | # created so far during each password generated so that later generators know how |
4106 | | - | # many additional typos, at most, they are permitted to add |
| 4111 | + | # many additional typos, at most, they are permitted to add, and also if it is |
| 4112 | + | # the last typo generator that will run, how many, at least, it *must* add |
4107 | 4113 | | global typos_sofar |
4108 | 4114 | | typos_sofar = 0 |
4109 | 4115 | | |
| skipped 10 lines |
4120 | 4126 | | |
4121 | 4127 | | # Copy a few globals into local for a small speed boost |
4122 | 4128 | | l_generator_product = generator_product |
4123 | | - | l_args_min_typos = args.min_typos |
4124 | 4129 | | l_regex_only = regex_only |
4125 | 4130 | | l_regex_never = regex_never |
4126 | 4131 | | l_password_dups = password_dups |
| skipped 11 lines |
4138 | 4143 | | if args.typos_insert: modification_generators.append( insert_typos_generator ) |
4139 | 4144 | | modification_generators_len = len(modification_generators) |
4140 | 4145 | | |
| 4146 | + | # Only the last typo generator needs to enforce a min-typos requirement |
| 4147 | + | if args.min_typos: |
| 4148 | + | assert modification_generators[-1] != expand_wildcards_generator |
| 4149 | + | # set the min_typos argument default value |
| 4150 | + | modification_generators[-1].func_defaults = (args.min_typos,) |
| 4151 | + | |
4141 | 4152 | | # The base password generator is set in parse_arguments(); it's either an iterable |
4142 | 4153 | | # or a generator function (which returns an iterator) that produces base passwords |
4143 | 4154 | | # usually based on either a tokenlist file (as parsed above) or a passwordlist file. |
| skipped 21 lines |
4165 | 4176 | | modification_iterator = (password_base,) |
4166 | 4177 | | |
4167 | 4178 | | for password in modification_iterator: |
4168 | | - | |
4169 | | - | if typos_sofar < l_args_min_typos: continue |
4170 | 4179 | | |
4171 | 4180 | | # Check the password against the --regex-only and --regex-never options |
4172 | 4181 | | if l_regex_only and not l_regex_only .search(password): continue |
| skipped 636 lines |
4809 | 4818 | | # capslock_typos_generator() is a generator function which tries swapping the case of |
4810 | 4819 | | # the entire password (producing just one variation of the password_base in addition |
4811 | 4820 | | # to the password_base itself) |
4812 | | - | def capslock_typos_generator(password_base): |
| 4821 | + | def capslock_typos_generator(password_base, min_typos = 0): |
4813 | 4822 | | global typos_sofar |
4814 | 4823 | | |
| 4824 | + | min_typos -= typos_sofar |
| 4825 | + | if min_typos > 1: return # this generator can't ever generate more than 1 typo |
| 4826 | + | |
4815 | 4827 | | # Start with the unmodified password itself, and end if there's nothing left to do |
4816 | | - | yield password_base |
| 4828 | + | if min_typos <= 0: yield password_base |
4817 | 4829 | | if typos_sofar >= args.typos: return |
4818 | 4830 | | |
4819 | 4831 | | password_swapped = password_base.swapcase() |
| skipped 7 lines |
4827 | 4839 | | # of the password_base where zero or more pairs of adjacent characters are swapped. Even |
4828 | 4840 | | # when multiple swapping typos are requested, any single character is never swapped more |
4829 | 4841 | | # than once per generated password. |
4830 | | - | def swap_typos_generator(password_base): |
| 4842 | + | def swap_typos_generator(password_base, min_typos = 0): |
4831 | 4843 | | global typos_sofar |
4832 | 4844 | | # Copy a few globals into local for a small speed boost |
4833 | 4845 | | l_xrange = xrange |
| skipped 1 lines |
4835 | 4847 | | l_args_nodupchecks = args.no_dupchecks |
4836 | 4848 | | |
4837 | 4849 | | # Start with the unmodified password itself |
4838 | | - | yield password_base |
| 4850 | + | min_typos -= typos_sofar |
| 4851 | + | if min_typos <= 0: yield password_base |
4839 | 4852 | | |
4840 | 4853 | | # First swap one pair of characters, then all combinations of 2 pairs, then of 3, |
4841 | 4854 | | # up to the max requested or up to the max number swappable (whichever's less). The |
4842 | 4855 | | # max number swappable is len // 2 because we never swap any single character twice. |
4843 | 4856 | | password_base_len = len(password_base) |
4844 | 4857 | | max_swaps = min(args.max_typos_swap, args.typos - typos_sofar, password_base_len // 2) |
4845 | | - | for swap_count in l_xrange(1, max_swaps + 1): |
| 4858 | + | for swap_count in l_xrange(max(1, min_typos), max_swaps + 1): |
4846 | 4859 | | typos_sofar += swap_count |
4847 | 4860 | | |
4848 | 4861 | | # Generate all possible combinations of swapping exactly swap_count characters; |
| skipped 47 lines |
4896 | 4909 | | # itself isn't very simple... it's called "simple" because the functions in the Configurables |
4897 | 4910 | | # section which simple_typos_generator() calls are simple; they are collectively called |
4898 | 4911 | | # simple typo generators) |
4899 | | - | def simple_typos_generator(password_base): |
| 4912 | + | def simple_typos_generator(password_base, min_typos = 0): |
4900 | 4913 | | global typos_sofar |
4901 | 4914 | | # Copy a few globals into local for a small speed boost |
4902 | 4915 | | l_xrange = xrange |
| skipped 4 lines |
4907 | 4920 | | assert len(enabled_simple_typos) > 0, "simple_typos_generator: at least one simple typo enabled" |
4908 | 4921 | | |
4909 | 4922 | | # Start with the unmodified password itself |
4910 | | - | yield password_base |
| 4923 | + | min_typos -= typos_sofar |
| 4924 | + | if min_typos <= 0: yield password_base |
4911 | 4925 | | |
4912 | 4926 | | # First change all single characters, then all combinations of 2 characters, then of 3, etc. |
4913 | 4927 | | password_base_len = len(password_base) |
4914 | 4928 | | max_typos = min(sum_max_simple_typos, args.typos - typos_sofar, password_base_len) |
4915 | | - | for typos_count in l_xrange(1, max_typos + 1): |
| 4929 | + | for typos_count in l_xrange(max(1, min_typos), max_typos + 1): |
4916 | 4930 | | typos_sofar += typos_count |
4917 | 4931 | | |
4918 | 4932 | | # Pre-calculate all possible permutations of the chosen simple_typos_choices |
| skipped 84 lines |
5003 | 5017 | | # insert_typos_generator() is a generator function which inserts one or more strings |
5004 | 5018 | | # from the typos_insert_expanded list between every pair of characters in password_base, |
5005 | 5019 | | # as well as at its beginning and its end. |
5006 | | - | def insert_typos_generator(password_base): |
| 5020 | + | def insert_typos_generator(password_base, min_typos = 0): |
5007 | 5021 | | global typos_sofar |
5008 | 5022 | | # Copy a few globals into local for a small speed boost |
5009 | 5023 | | l_max_adjacent_inserts = args.max_adjacent_inserts |
| skipped 1 lines |
5011 | 5025 | | l_itertools_product = itertools.product |
5012 | 5026 | | |
5013 | 5027 | | # Start with the unmodified password itself |
5014 | | - | yield password_base |
| 5028 | + | min_typos -= typos_sofar |
| 5029 | + | if min_typos <= 0: yield password_base |
5015 | 5030 | | |
5016 | 5031 | | password_base_len = len(password_base) |
5017 | 5032 | | assert l_max_adjacent_inserts > 0 |
| skipped 7 lines |
5025 | 5040 | | max_inserts = min(args.max_typos_insert, args.typos - typos_sofar, password_base_len + 1) |
5026 | 5041 | | |
5027 | 5042 | | # First insert a single string, then all combinations of 2 strings, then of 3, etc. |
5028 | | - | for inserts_count in l_xrange(1, max_inserts + 1): |
| 5043 | + | for inserts_count in l_xrange(max(1, min_typos), max_inserts + 1): |
5029 | 5044 | | typos_sofar += inserts_count |
5030 | 5045 | | |
5031 | 5046 | | # Select the indexes (some possibly the same) of exactly inserts_count characters |
| skipped 539 lines |