classification
Title: ALternative recipe for password using secrets
Type: enhancement Stage:
Components: Documentation Versions: Python 3.6
process
Status: open Resolution:
Dependencies: Superseder:
Assigned To: docs@python Nosy List: docs@python, jpc4242
Priority: normal Keywords:

Created on 2018-04-25 22:58 by jpc4242, last changed 2018-04-26 07:05 by jpc4242.

Messages (2)
msg315763 - (view) Author: Juan Postlbauer (jpc4242) Date: 2018-04-25 22:58
Chapter 15.3.4 shows a recipe with an infinite potential loop.
An alternative would be:

''.join(sorted([choice(string.ascii_lowercase) for i in range(1)]+[choice(string.ascii_uppercase) for i in range(1)]+[choice(string.digits) for i in range(3)]+[choice(string.ascii_letters+string.digits) for i in range(10-(1+1+3))],key=lambda x:randbelow(4096)))

Can we assume secrets.SystemRandom is cryptographically strong but has all the methods of random??
If so it can be done in a more understandable way by using choices and shuffle. (see 2 examples below)

def generate_password_random(totalchars=10,minlower=1,minupper=1,mindigits=3):
    restcount= totalchars-(minlower+minupper+mindigits)
    if restcount<0:
        raise ValueError("Impossible conditions")
    lowerchars=random.choices(string.ascii_lowercase,k=minlower)
    upperchars=random.choices(string.ascii_uppercase,k=minupper)
    digitchars=random.choices(string.digits,k=mindigits)
    restchars=random.choices(string.ascii_letters+string.digits,k=restcount)
    allchars=lowerchars+upperchars+digitchars+restchars
    random.shuffle(allchars)
    password=''.join(allchars)
    return password

def generate_password_secrets(totalchars=10,minlower=1,minupper=1,mindigits=3):
    restcount= totalchars- (minlower+minupper+mindigits)
    if restcount<0:
        raise ValueError("Impossible conditions")
    lowerchars=[secrets.choice(string.ascii_lowercase) for _ in range(minlower)]
    upperchars=[secrets.choice(string.ascii_uppercase) for _ in range(minupper)]
    digitchars=[secrets.choice(string.digits) for _ in range (mindigits)]
    restchars=[secrets.choice(string.ascii_letters+string.digits) for _ in range (restcount)]
    allchars=lowerchars+upperchars+digitchars+restchars
    allchars.sort(key=lambda x:secrets.randbelow(4096))
    password=''.join(allchars)
    return password
msg315780 - (view) Author: Juan Postlbauer (jpc4242) Date: 2018-04-26 07:05
Just a clarification: by "infinite potential loop" I meant a loop that *theoretically* could last forever. Of course in practice it won't, but my experiments show that for the conditions in the example in average the current recipe generates 5 tentative passwords before finding one that fulfills all conditions.
History
Date User Action Args
2018-04-26 07:05:10jpc4242setmessages: + msg315780
2018-04-25 22:58:37jpc4242create