I wrote a password generation script, it ensures that 2 characters of the same type cannot appear behind each other: “12” or “ab” are not possible. It always have to be different types, like “1a” or “a%” or “aB”.
I currently use this to achieve it:
# l1 - l4 are the last 4 symbols, l5 the current.
# n1 - n5 are just ones and zeros, depending if l on that position is a number
# For the problem I describe here just l5 and l4 would be necessary, but I have different 'policies',
# if you want 2 or 3 characters of the same type behind each other
if (l1.isupper() and l2.isupper()) or # characters upper
(l2.isupper() and l3.isupper()) or
(l3.isupper() and l4.isupper()) or
(l4.isupper() and l5.isupper()) or
(l1.islower() and l2.islower()) or
(l2.islower() and l3.islower()) or
(l3.islower() and l4.islower()) or
(l4.islower() and l5.islower()) or # characters lower
(n1 and n2) or (n2 and n3) or (n3 and n4) or (n4 and n5): # consecutive numbers
return False
While it does work its rather slow, because if the function returns False a new random character is sampled and tested again until a valid character is found.
I have thought about instead of having one giant string of characters ,
char_list = ['A', ...'Z', 'a', ...'z', '0', ...'9', '!', ...'+'] # !...+ are symbols like !§$ and others
I could split it into types, like so:
char_list = [['A', ...'Z'], ['a', ...'z'], ['0', ...'9'], ['!', ...'+']]
The generator could pick one of the lists, then sample a character and simply disable this type for the next iteration. This should be much faster because it wouldn’t be possible to have the same type twice in a row. I however have no idea how to implement this ‘disable for one cycle’ thing.
Full code: https://pastebin.com/ZJkaKcni
Advertisement
Answer
Well you could do something like this, I’m sure it is not the most performance optimized solution but is fairly fast, although I don’t know what is slow/fast for your usecase:
import random
import string
def generate_password(length):
password = ""
char_pools = [
string.ascii_lowercase,
string.ascii_uppercase,
string.digits,
string.punctuation,
]
prev_pool = None
pool = random.choice(char_pools)
for _ in range(length):
while pool == prev_pool:
pool = random.choice(char_pools)
password += random.choice(pool)
prev_pool = pool
return password
From cryptographic perspective though I’m not sure if this is a safe solution, since you already give a hint to the attacker. If they could identify a character from the password, they were able to presume the next set of possible characters which can lead to a decryption already faster than a brute-force attack.