AoC 2016 Day 5: Password Cracker

Source: How About a Nice Game of Chess?

Part 1: Generate a series of hashes: MD5(salt + index). For each hash starting with five zeros, write down the sixth character.

First, a helper function to generate an infinite list of natural numbers:

def naturals(i = 0):
    while True:
        yield i
        i += 1

I have since learned that this function already exists in the standard library: itertools.count . So it goes.

Then a second helper that will take a string and return its MD5 hash (in hex format):

def md5(str):
    return hashlib.md5(str.encode()).hexdigest()

Then we just have to keep going until we have enough of a password:

password = ''

for i in naturals():
    hash = md5(args.salt + str(i))

    if hash[:5] == '00000':
        password += hash[5]

    if len(password) == 8:


Part 2: The sixth character now represents the position (0-7) in the password to write (only use the first write to each position; ignore 8-F as the sixth character). The seventh is the actual password character.

This one is somewhat more interesting. You have to:

  • Determine where the new character would go and if that spot is available
  • Determine what the new character would be
  • Figure out when we’ve found all 8 characters
hard_password = ['-'] * 8

for i in naturals():
    hash = md5(args.salt + str(i))

    if hash[:5] == '00000':
        index = hash[5]
        if index not in '01234567':

        index = int(index)
        if hard_password[index] != '-':

        hard_password[index] = hash[6]

    if not(any(c == '-' for c in hard_password)):


I like Python’s any function. It reminds me of more functional programming paradigms.

On fun thing you can do with the full source for today’s solution is watch the progress as it cracks the password:

Skipping (many) frames to keep the file size reasonable. Created with asciinema, asciicast2gif, and gifsicle… That should be easier.