I\'m interested in creating a very simple, high (cryptographic) quality random password generator. Is there a better way to do this?
import os, random, strin
The difficult thing with passwords is to make them strong enough and still be able to remember them. If the password is not meant to be remembered by a human being, then it is not really a password.
You use Python's os.urandom()
: that's good. For any practical purpose (even cryptography), the output of os.urandom()
is indistinguishable from true alea. Then you use it as seed in random
, which is less good: that one is a non-cryptographic PRNG, and its output may exhibit some structure which will not register in a statistical measurement tool, but might be exploited by an intelligent attacker. You should work with os.urandom()
all along. To make things simple: choose an alphabet of length 64, e.g. letters (uppercase and lowercase), digits, and two extra punctuation characters (such as '+' and '/'). Then, for each password character, get one byte from os.urandom()
, reduce the value modulo 64 (this is unbiased because 64 divides 256) and use the result as index in your chars
array.
With an alphabet of length 64, you get 6 bits of entropy per character (because 26 = 64). Thus, with 13 characters, you get 78 bits of entropy. This is not ultimately strong in all cases, but already very strong (it could be defeated with a budget which will be counted in months and billions of dollars, not mere millions).
XKCD has a great explanation of why what you think are strong passwords aren't.
To anyone who understands information theory and security and is in an infuriating argument with someone who does not (possibly involving mixed case), I sincerely apologize. - Randall Munroe
And if you don't understand the math behind what this illustration is explaining, don't try writing anything that should be cryptographically secure, because it won't be. Just put the mouse down and step away from the keyboard.
Considering your comment,
I just need to be able to generate passwords that are more secure than the ones I would come up with in my head.
it seems you want to use your program to generate passwords, rather than just writing it as an exercise. It is preferable to use an existing implementation, because if you make a mistake, the output might be compromised. Read about random number generator attacks; in particular, a well-known RNG bug in Debian exposed people's SSL private keys.
So instead, consider using pwgen. It provides several options, which you should choose depending on what you plan to use the passwords for.
My solution based on @Thomas Pornin's answer (Updated)
import os, string
def get_pass(password_len=12):
new_password=None
symbols='+!'
chars=string.ascii_lowercase+\
string.ascii_uppercase+\
string.digits+\
symbols
while new_password is None or \
new_password[0] in string.digits or \
new_password[0] in symbols:
new_password=''.join([chars[ord(os.urandom(1)) % len(chars)] \
for i in range(password_len)])
return new_password
print(get_pass())
This function returns a random password (without a number or a symbol in beginning of the password).
Here is my random password generator after researching this topic:
`import os, random, string
#Generate Random Password
UPP = random.SystemRandom().choice(string.ascii_uppercase)
LOW1 = random.SystemRandom().choice(string.ascii_lowercase)
LOW2 = random.SystemRandom().choice(string.ascii_lowercase)
LOW3 = random.SystemRandom().choice(string.ascii_lowercase)
DIG1 = random.SystemRandom().choice(string.digits)
DIG2 = random.SystemRandom().choice(string.digits)
DIG3 = random.SystemRandom().choice(string.digits)
SPEC = random.SystemRandom().choice('!@#$%^&*()')
PWD = None
PWD = UPP + LOW1 + LOW2 + LOW3 + DIG1 + DIG2 + DIG3 + SPEC
PWD = ''.join(random.sample(PWD,len(PWD)))
print(PWD)`
This will generate a random password with 1 random uppercase letter, 3 random lowercase letters, 3 random digits, and 1 random special character--this can be adjusted as needed. Then it combines each random character and creates a random order. I don't know if this is considered "high quality", but it gets the job done.
I just recently started learning python and this is something I wrote today. Hope this helps.
import random
characters = 'abcdefghijklmnopqrstuvwxyz1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^()}{/<>'
print('Password Length: ')
passwordLength = int(input())
password = ''
for i in range(passwordLength):
password += random.choice(characters)
print(password)