Wednesday, May 7, 2014

Generating and learning strong passwords, Python version

Just for fun, I rewrote my password shell scripts in Python. These scripts work essentially the same way and are compatible with the scripts from the previous post. The real purpose of this exercise is to give you the chance to compare some of the syntax and features of the languages.

This is my first post using Python, but I’m not going to explain all of the basics of Python syntax here. If you aren’t familiar with Python, you may want to read a tutorial or introduction to the language first.

Here is what genpw.py looks like:

#!/usr/bin/python3

import random
import sys

chars = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', '_', '`', '{', '|', '}', '~' ]
length = 13
pw = ""

for i in range(1, len(sys.argv)):
    if sys.argv[i] == "--alnum":
        chars = chars[0:62]
    elif sys.argv[i] == "--length":
        length = int(sys.argv[i+1])
    elif sys.argv[i] == "--forbidden":
        forbiddenchars = sys.argv[i+1]
        for ch in forbiddenchars:
            chars.remove(ch)

end = len(chars) - 1

for i in range(0, length):
    index =  random.randint(0, end)
    ch = chars[index]
    pw += ch

print(pw)


First, we start with a list of printable ascii characters. Since it has been sorted into numbers, letters, and symbols; we can get the alphanumeric subset with a simple slice operation. And we can remove any forbidden characters by simply calling the remove() method of the list of characters. Then, we find how many characters are in the resulting list and generate a random number within that range for each character in the final password. We simply use that number as the index to choose one of the characters from our list and then print the completed password string.

The approach here is slightly different from the one that I used in the Bash script. There, we generated a stream of random numbers and filtered out only the desired characters until we had the desired length. Here, we create a list of desired characters and select the desired number of characters randomly.

And here is what setpw.py looks like:

#!/usr/bin/python3

import sys
import os
import hashlib

if len(sys.argv) > 1:
    name = sys.argv[1]
else:
    name = "default"

pw = input().encode('ascii')
pwhash = hashlib.sha512(pw).hexdigest()
hashfile = os.path.expanduser("~/." + name + "pwhash")

print("\033[1A\033[0K", end="")
with open(hashfile, 'w') as f:
    f.write(pwhash)


This one is even more similar to the Bash version, it’s approach does not substantially differ.

Finally, here is what ppw.py looks like:

#!/usr/bin/python3

import sys
import os
import hashlib
import getpass

if len(sys.argv) > 1:
    name = sys.argv[1]
else:
    name = "default"

pw = getpass.getpass("").encode('ascii')
pwhash = hashlib.sha512(pw).hexdigest()
hashfile = os.path.expanduser("~/." + name + "pwhash")

with open(hashfile, 'r') as f:
    storedhash = f.read()

if pwhash == storedhash:
    print("Correct")
else:
    print("Wrong\a")


Again, the approach here is essentially the same as the Bash version. You may notice that instead of input(), I used getpass.getpass(""). This prevents it from echoing the characters to the terminal. By default, getpass() uses the prompt Password:, so to make it more consistent with the Bash version I passed it an empty string instead.

Hopefully you found this little exercise entertaining and educational.

No comments:

Post a Comment