Skip to content

Commit

Permalink
Fix long non-ambiguous passwords
Browse files Browse the repository at this point in the history
If no_ambiguous is set, the ambiguous characters are now replaced one
by one instead of throwing away the password and trying to generate
a new one. This was a problem when generating long, non-ambiguous
passwords.

In addition, when symbols, numerals or capital letters are enforced,
these characters will no longer be inserted at random positions in
the password. Instead, they will only replace lowercase letters, if
available. Otherwise, it was possible to e.g. overwrite the single
numeral with a symbol if numerals and symbols are set and only one
numeral but no symbols were present in the password (closes vinces1979#13).
  • Loading branch information
MichiK committed Jul 28, 2018
1 parent b86d7d5 commit 583ba32
Showing 1 changed file with 33 additions and 7 deletions.
40 changes: 33 additions & 7 deletions pwgen/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
2015 - Luar was here <[email protected]>
"""

import string
import re

Expand All @@ -23,20 +22,35 @@
Digits = string.digits
Symbols = string.punctuation

HasLowercase = re.compile("[a-z]")
HasCaps = re.compile("[A-Z]")
HasNumerals = re.compile("[0-9]")
HasSymbols = re.compile(r"[%s]" % re.escape(Symbols))
HasAmbiguous = re.compile("[B8G6I1l|0OQDS5Z2]")


def replaceRandomChar(letter, word, pos=None):
if not pos:
"""Replace a character in the password with another.
@param letter: The character to insert into the password
@param word: The password to insert the letter into
@param pos: The position the letter shall be inserted at (may be a
position or a list of positions to choose from randomly)
"""
if isinstance(pos, list) and len(pos) > 0:
pos = choice(pos)
elif pos is not None:
pos = randint(0, len(word)-1)
word = list(word)
word[pos] = letter
return "".join(word)


def findAllChars(word, pattern):
"""Return a list of positions in the string where the pattern matches."""
return [m.start() for m in re.finditer(pattern, word)]


def pwgen(pw_length=20, num_pw=1, no_numerals=False, no_capitalize=False,
capitalize=False, numerals=False, no_symbols=True, symbols=False,
allowed_symbols=None, no_ambiguous=False):
Expand Down Expand Up @@ -76,13 +90,25 @@ def pwgen(pw_length=20, num_pw=1, no_numerals=False, no_capitalize=False,
while len(passwds) < int(num_pw):
passwd = "".join(choice(letters) for x in range(pw_length))
if capitalize and not HasCaps.search(passwd):
passwd = replaceRandomChar(choice(UpperCase), passwd)
passwd = replaceRandomChar(
choice(UpperCase), passwd,
pos=findAllChars(passwd, HasLowercase)
)
if numerals and not HasNumerals.search(passwd):
passwd = replaceRandomChar(choice(Digits), passwd)
passwd = replaceRandomChar(
choice(Digits), passwd,
pos=findAllChars(passwd, HasLowercase)
)
if symbols and not HasSymbols.search(passwd):
passwd = replaceRandomChar(choice(Symbols), passwd)
if no_ambiguous and HasAmbiguous.search(passwd):
continue
passwd = replaceRandomChar(
choice(Symbols), passwd,
pos=findAllChars(passwd, HasLowercase)
)
while no_ambiguous and HasAmbiguous.search(passwd):
passwd = replaceRandomChar(
choice(letters), passwd,
pos=findAllChars(passwd, HasAmbiguous)
)
passwds.append(passwd)

if len(passwds) == 1:
Expand Down

0 comments on commit 583ba32

Please sign in to comment.