#!/usr/bin/env python3
from hashlib import sha256
from sys import argv
PASS_LEN = 20
def nextgr(): # Detrmine next group
global j, m, C
f = j # Remember previous group
while f == j: # Get new group that is not the same as previous
j = m % len(C)
m //= len(C)
#print(j, m)
return j
if __name__ == '__main__':
try:
a = argv[1]
except:
print('Usage: pwcreate <pass_phrase_without_spaces>')
else:
C = ['0123456789',
',.<>?;:!@#$%^&*()_+-|~`[]{}',
'abcdefghijklmnopqrstuvwxyz',
'ABCDEFGHIJKLMNOPQRSTUVWXYZ']
h = sha256(a.encode('utf-8')).digest() # Get 32 bytes digest
n = int.from_bytes(h[:18], 'big') # Take 18 bytes for for characters choiсe
m = int.from_bytes(h[18:], 'big') # Take rest bytes fog groups choiсe
p, j = '', None
k = C[nextgr()] # Get first group
while n > len(k):
p += k[n % len(k)]
n //= len(k)
#print(j, p, n)
if len(p) >= PASS_LEN: # Stop if lenght is enought
break
k = C[nextgr()] # Get next group
print (p) # Output the result
Берем хеш подлиннее (256 бит) делим его на две неравные части.
Меньшую используем для определения группы символов, а большую - для определения символа в группе.
Причем группу мы каждый раз выбираем отличную от предыдущей - т.е. в пароле никогда не будут следовать подряд символы из одной группы (к примеру, если последний символ был заглавной буквой, то следующей будет либо цифра, либо прописная буква, либо спец.символ.
Тут еще больший кавардак получается в том, что большую часть от хеша мы преобразуем по разным модулям почти на каждом шаге т.к. только группы прописных и заглавных имеют одинаковую длинну.
Но, не смотря на кажущуюся стохастичность в работе алгоритма - он полностью детерминирован (т.е. на одном входе всегда дает одинаковый выхлоп).