Programming Project*
Generating a Password XKCD Style

iPython Notebook due Thursday Week 5

 


Introduction

Nobody likes to change their password, and its always hard trying to come up with a new password. In this project, we'll solve that problem using a solution posed by the popular XKCD comic.


Exercises and Experiments

Create a new notebook for this project in Google Colab.

Password 1: Random characters

  1. Start by defining the following three strings:
        letters = "abcdefghijklmnopqrstuvwxyz"
        caps = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        numbers = "1234567890"
        special = "!?-%$#*~"
        
  2. The first password to create is an 8-character sequence, comprised of random characters from among the letters, caps, numbers, and special strings that were defined above. One algorithm for doing this is as follows:
    • Create one long string containing all of the possible characters that can be included in a password. (Hint: Do this by adding the strings together, not by retyping all of the characters!)
    • Create a variable, passwd1, that will be used to hold the password and initialize it to the empty string.
    • To determine each of the 8 characters in the password, repeat the following:
      • Get a random number between 0 and the length of the long string. (Remember, if a string had 5 characters in it, they occupy positions 0, 1, 2, 3, and 4.)
      • Use that random number to get a character from the long string.
      • Add that character to the password.
    • Print out the password you created.

    • Run the code several times to see that you are getting different results each time.

  3. In a Text cell, write a sentence to answer the question: How many different passwords could you create using this method?

Password 2: Random Words

  1. In a new Code cell, define the following lists of nouns, verbs, and adjectives:
        nouns = ['tissue', 'processor', 'headquarters', 'favorite', 'cure',
        'ideology', 'funeral', 'engine', 'isolation', 'perception', 'hat',
        'mountain', 'session', 'case', 'legislature', 'consent', 'spread',
        'shot', 'direction', 'data', 'tragedy', 'illness', 'serving',
        'mess', 'resistance', 'basis', 'kitchen', 'mine', 'temple', 'mass',
        'dot', 'final', 'chair', 'picture', 'wish', 'transfer',
        'profession', 'suggestion', 'purse', 'rabbit', 'disaster', 'evil',
        'shorts', 'tip', 'patrol', 'fragment', 'assignment', 'view',
        'bottle', 'acquisition', 'origin', 'lesson', 'Bible', 'act',
        'constitution', 'standard', 'status', 'burden', 'language', 'voice',
        'border', 'statement', 'personnel', 'shape', 'computer', 'quality',
        'colony', 'traveler', 'merit', 'puzzle', 'poll', 'wind', 'shelter',
        'limit', 'talent']
            
        verbs = ['represent', 'warm', 'whisper', 'consider', 'rub', 'march',
        'claim', 'fill', 'present', 'complain', 'offer', 'provoke', 'yield',
        'shock', 'purchase', 'seek', 'operate', 'persist', 'inspire',
        'conclude', 'transform', 'add', 'boast', 'gather', 'manage',
        'escape', 'handle', 'transfer', 'tune', 'born', 'decrease',
        'impose', 'adopt', 'suppose', 'sell', 'disappear', 'join', 'rock',
        'appreciate', 'express', 'finish', 'modify', 'keep', 'invest',
        'weaken', 'speed', 'discuss', 'facilitate', 'question', 'date',
        'coordinate', 'repeat', 'relate', 'advise', 'arrest', 'appeal',
        'clean', 'disagree', 'guard', 'gaze', 'spend', 'owe', 'wait',
        'unfold', 'back', 'waste', 'delay', 'store', 'balance', 'compete',
        'bake', 'employ', 'dip', 'frown', 'insert']
        
        adjs = ['busy', 'closer', 'national', 'pale', 'encouraging',
        'historical', 'extreme', 'cruel', 'expensive', 'comfortable',
        'steady', 'necessary', 'isolated', 'deep', 'bad', 'free',
        'voluntary', 'informal', 'loud', 'key', 'extra', 'wise', 'improved',
        'mad', 'willing', 'actual', 'OK', 'gray', 'little', 'religious',
        'municipal', 'just', 'psychological', 'essential', 'perfect',
        'intense', 'blue', 'following', 'Asian', 'shared', 'rare',
        'developmental', 'uncomfortable', 'interesting', 'environmental',
        'amazing', 'unhappy', 'horrible', 'philosophical', 'American']
        
  2. The next password to create is a 4-word password that is a concatenation of four words from among the nouns, verbs, and adjs lists above. The algorithm to do this is very similar to the one for creating the 8-character password. Store the password in a new variable, passwd2.

  3. Run your program several times to see that the output changes each time.

  4. In a new Text cell, write sentence(s) to answer the following questions:
    • How many possible passwords can you create using this method? (It may be helpful to know the length of these lists! Use the len function to help you determine these sizes.)
    • Is every password guaranteed to have at least one noun, verb, and adjective? Why or why not?
    • Describe what changes you might need to make so that each password has at least one noun, verb, and adjective. You do not need to make the changes to the code, but if you do, please copy the code you have, and paste it into another Code cell to make the changes.

Password 3: Forcing capitals and numbers

In the password that was generated as a concatenation of 4 random words, some of them may happen to have a capital letter, but none of them will have a number. Many IT departments require a capital letter and a number. In these exercises, we will modify your word lists and will add some checks to the password you generated. If the password did not contain a capital letter or a number, you will replace one or more characters to ensure that it does.
  1. Modify some of the words in your word lists so that they start with capital letters.

  2. After your code that generates password2, create a boolean variable named containsCap and initialize it to False.

  3. Create another variable, passwd3, for the new password you are going to create and initialize it to the password you just created. (Hint: Your code would look like passwd3 = passwd2)

  4. For each character in this new password, check if it is a capital letter or not. (You can check if the character is in the caps string (from above).) If it is, set your boolean variable to be True.

  5. Once you have checked all of the characters, if none of them were capitalized (i.e., containsCap is still False), we now need to replace a random character of our password with a capital letter. Create a random number for the position of the character in the password to be replaced. (This would be a number between 0 and the length of the password.)

  6. Tp replace a character in a string, you may use the slice operator on a string to separate the string into pieces. For example if we have a string, person, defined by
    person = "baker"
    we could replace the 'a' with an 'i' to make the word "biker" by doing the following:
    newPerson = person[:1] + "i" + person[2:]
            
    The first term in that sum, person[:1], takes all of the characters from the beginning of the string, up to, but not including the character at position 1. So this gives us the substring "b". We then concatenate the "i", and then concatenate the rest of the original word. The last term, person[2:], gives all the characters from position 2 to the end of the string, giving us the substring "ker".
    As another example, suppose we wanted to change the word into "baked". We could do this with the following statement:
    newPerson = person[:-1] + "d"
    Using the random number you generated in the previous step, replace the letter of your password in that position with the corresponding capital letter. You will need to use the index() function to determine where in the alphabet the letter to be replaced is. For example, if the lowercase "m" in the word "pumpkin" is to be replaced with a capital "M", the code might look something like the following:
                    word = "pumpkin"
                    letterToBeReplaced = word[2]
                    positionInAlphabet = letters.index(letterToBeReplaced)
                    newWord = word[:2] + caps[positionInAlphabet] + word[3:]
                    print(newWord)
                    
    The output from this code segment should be
    puMpkin
    Print out your password to check that it looks the way you expect at this point.

  7. Now that we have forced the password to have a capital letter, use similar steps to force the password to have a number. This should be easier to do:
    • Generate a random position in the password. If the character in this position of your password is a capital letter, generate a different random position. Continue to re-generate the random number until you find a position that does not have a capital letter. (Hint: You may want to use a while loop here. See Sections 14.2 and 14.3 in the Runestone book for more information on while loops.)

    • Replace the character at that position with a random number from the numbers list.

    • Print out your new password to check that it looks like you expect at this point.

  8. Now use the same technique to replace one of the characters in the password with a special character. DO NOT replace the number or the capital letter that you just made sure the password contained.

  9. Print your password.

  10. (Optional Challenge): It is a common technique to replace the letter l with the number 1, or the letter e with the number 3, or the letter o with the number 0. Instead of picking a random position to put the number, try replacing one or more of these letters with their corresponding numbers.

Password 4: Guessing weak passwords

Supose we are given a 4-character password composed only of lowercase letters. What would it take to create a 4-character string and compare it to the known password? How many guesses would it take? In the next set of exercises, we will work through guessing a random 4-letter password.
  1. In a new Code cell, copy the line of code that defines the string letters. This should be a string containing the 26 lower case letters.

  2. Generate a random 4-letter password by doing something like this:
        my_password=""
        for i in range(4):
            randNum = random.randint(0,25)
            randLetter = letters[randNum]
            my_password += randLetter
        
    You should print the password and run this code to verify it creates a different 4-letter password each time. Then comment out the print statement.

  3. Create a new variable, call it password_guess, and initialize it to the empty string.

  4. Create a variable that will be used to keep track of the number of guesses we need until we find a match with the generated password. Initialize this variable to 0.

  5. We will now use 4 nested for loops to go through all possible 4-letter combinations which we will compare to the generated 4-letter password. Each loop will have the same structure:
    for char1 in letters
    where char1 is replaced with char2 in the second loop, char3 in the third loop, and so on. Write the code for these loops.

  6. In the body of the 4th, inner-most loop, we will do the following:
    • Assign password_guess the concatenation of char1, char2, char3, and char4.
    • Increment the variable that is storing the number of guesses.
    • Check if that guess is equal to the generated password. If it is, print out a message that the password has been found and how many guesses it took to find it.

  7. The password guesses will run through the possibilities in the order 'aaaa', 'aaab', 'aaac', ..., 'aaaz', 'aaba', 'aabb', 'aabc', ..., 'zzzy', 'zzzz'. What is the best case for guessing the password and how many guesses would that take? What is the worst case for guessing the password and how many guesses would that take? Write your answers to these questions in a new Text cell.

Submitting Your Project

When you are satisfied your code is working properly, submit your notebook via Kit. Your Python code will be graded on the basis of style as well as correctness. Your code should include appropriate comments. (Comments before code sections, name, date, program description, assistance statement at the beginning of the file, etc.) Variable names should be descriptive.


* This project is based on a project with the same name in the Runestone book, Foundations of Python Programming (back to top)